EditedSource.cpp revision 3f6f51e28231f65de9c2dd150a2d757b2162cfa3
15821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)//===----- EditedSource.cpp - Collection of source edits ------------------===// 25821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// 35821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// The LLVM Compiler Infrastructure 45821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// 55821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// This file is distributed under the University of Illinois Open Source 62a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// License. See LICENSE.TXT for details. 75821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// 85821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)//===----------------------------------------------------------------------===// 95821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 102a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "clang/Edit/EditedSource.h" 112a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "clang/Basic/CharInfo.h" 125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "clang/Basic/SourceManager.h" 132a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "clang/Edit/Commit.h" 145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "clang/Edit/EditsReceiver.h" 155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "clang/Lex/Lexer.h" 165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "llvm/ADT/SmallString.h" 175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "llvm/ADT/Twine.h" 185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 192a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)using namespace clang; 202a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)using namespace edit; 212a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) 225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void EditsReceiver::remove(CharSourceRange range) { 235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) replace(range, StringRef()); 245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)} 255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)StringRef EditedSource::copyString(const Twine &twine) { 272a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) SmallString<128> Data; 285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return copyString(twine.toStringRef(Data)); 292a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)} 305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)bool EditedSource::canInsertInOffset(SourceLocation OrigLoc, FileOffset Offs) { 325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) FileEditsTy::iterator FA = getActionForOffset(Offs); 335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (FA != FileEdits.end()) { 345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (FA->first != Offs) 355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return false; // position has been removed. 365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (SourceMgr.isMacroArgExpansion(OrigLoc)) { 395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) SourceLocation 405f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) DefArgLoc = SourceMgr.getImmediateExpansionRange(OrigLoc).first; 415f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) SourceLocation 425f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) ExpLoc = SourceMgr.getImmediateExpansionRange(DefArgLoc).first; 435f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) llvm::DenseMap<unsigned, SourceLocation>::iterator 445f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) I = ExpansionToArgMap.find(ExpLoc.getRawEncoding()); 455f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) if (I != ExpansionToArgMap.end() && I->second != DefArgLoc) 465f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) return false; // Trying to write in a macro argument input that has 475f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) // already been written for another argument of the same macro. 485f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) } 495f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) 505f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) return true; 512a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)} 525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)bool EditedSource::commitInsert(SourceLocation OrigLoc, 545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) FileOffset Offs, StringRef text, 552a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) bool beforePreviousInsertions) { 565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (!canInsertInOffset(OrigLoc, Offs)) 575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return false; 585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (text.empty()) 595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return true; 605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (SourceMgr.isMacroArgExpansion(OrigLoc)) { 625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) SourceLocation 635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) DefArgLoc = SourceMgr.getImmediateExpansionRange(OrigLoc).first; 642a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) SourceLocation 655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) ExpLoc = SourceMgr.getImmediateExpansionRange(DefArgLoc).first; 662a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) ExpansionToArgMap[ExpLoc.getRawEncoding()] = DefArgLoc; 675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) FileEdit &FA = FileEdits[Offs]; 705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (FA.Text.empty()) { 715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) FA.Text = copyString(text); 725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return true; 735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 742a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) 755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) Twine concat; 762a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) if (beforePreviousInsertions) 772a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) concat = Twine(text) + FA.Text; 782a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) else 795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) concat = Twine(FA.Text) + text; 805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) FA.Text = copyString(concat); 825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return true; 835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)} 845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)bool EditedSource::commitInsertFromRange(SourceLocation OrigLoc, 865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) FileOffset Offs, 875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) FileOffset InsertFromRangeOffs, unsigned Len, 885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) bool beforePreviousInsertions) { 895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (Len == 0) 902a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) return true; 912a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) 922a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) SmallString<128> StrVec; 935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) FileOffset BeginOffs = InsertFromRangeOffs; 945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) FileOffset EndOffs = BeginOffs.getWithOffset(Len); 955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) FileEditsTy::iterator I = FileEdits.upper_bound(BeginOffs); 965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (I != FileEdits.begin()) 975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) --I; 985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) for (; I != FileEdits.end(); ++I) { 1005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) FileEdit &FA = I->second; 1015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) FileOffset B = I->first; 1025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) FileOffset E = B.getWithOffset(FA.RemoveLen); 1032a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) 1045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (BeginOffs == B) 1052a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) break; 1062a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) 1072a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) if (BeginOffs < E) { 1085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (BeginOffs > B) { 1095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) BeginOffs = E; 1102a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) ++I; 1112a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) } 1122a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) break; 1135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 1145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 1155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) for (; I != FileEdits.end() && EndOffs > I->first; ++I) { 1175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) FileEdit &FA = I->second; 1185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) FileOffset B = I->first; 1195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) FileOffset E = B.getWithOffset(FA.RemoveLen); 1205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (BeginOffs < B) { 1225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) bool Invalid = false; 1235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) StringRef text = getSourceText(BeginOffs, B, Invalid); 1242a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) if (Invalid) 1252a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) return false; 1262a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) StrVec += text; 127 } 128 StrVec += FA.Text; 129 BeginOffs = E; 130 } 131 132 if (BeginOffs < EndOffs) { 133 bool Invalid = false; 134 StringRef text = getSourceText(BeginOffs, EndOffs, Invalid); 135 if (Invalid) 136 return false; 137 StrVec += text; 138 } 139 140 return commitInsert(OrigLoc, Offs, StrVec.str(), beforePreviousInsertions); 141} 142 143void EditedSource::commitRemove(SourceLocation OrigLoc, 144 FileOffset BeginOffs, unsigned Len) { 145 if (Len == 0) 146 return; 147 148 FileOffset EndOffs = BeginOffs.getWithOffset(Len); 149 FileEditsTy::iterator I = FileEdits.upper_bound(BeginOffs); 150 if (I != FileEdits.begin()) 151 --I; 152 153 for (; I != FileEdits.end(); ++I) { 154 FileEdit &FA = I->second; 155 FileOffset B = I->first; 156 FileOffset E = B.getWithOffset(FA.RemoveLen); 157 158 if (BeginOffs < E) 159 break; 160 } 161 162 FileOffset TopBegin, TopEnd; 163 FileEdit *TopFA = 0; 164 165 if (I == FileEdits.end()) { 166 FileEditsTy::iterator 167 NewI = FileEdits.insert(I, std::make_pair(BeginOffs, FileEdit())); 168 NewI->second.RemoveLen = Len; 169 return; 170 } 171 172 FileEdit &FA = I->second; 173 FileOffset B = I->first; 174 FileOffset E = B.getWithOffset(FA.RemoveLen); 175 if (BeginOffs < B) { 176 FileEditsTy::iterator 177 NewI = FileEdits.insert(I, std::make_pair(BeginOffs, FileEdit())); 178 TopBegin = BeginOffs; 179 TopEnd = EndOffs; 180 TopFA = &NewI->second; 181 TopFA->RemoveLen = Len; 182 } else { 183 TopBegin = B; 184 TopEnd = E; 185 TopFA = &I->second; 186 if (TopEnd >= EndOffs) 187 return; 188 unsigned diff = EndOffs.getOffset() - TopEnd.getOffset(); 189 TopEnd = EndOffs; 190 TopFA->RemoveLen += diff; 191 ++I; 192 } 193 194 while (I != FileEdits.end()) { 195 FileEdit &FA = I->second; 196 FileOffset B = I->first; 197 FileOffset E = B.getWithOffset(FA.RemoveLen); 198 199 if (B >= TopEnd) 200 break; 201 202 if (E <= TopEnd) { 203 FileEdits.erase(I++); 204 continue; 205 } 206 207 if (B < TopEnd) { 208 unsigned diff = E.getOffset() - TopEnd.getOffset(); 209 TopEnd = E; 210 TopFA->RemoveLen += diff; 211 FileEdits.erase(I); 212 } 213 214 break; 215 } 216} 217 218bool EditedSource::commit(const Commit &commit) { 219 if (!commit.isCommitable()) 220 return false; 221 222 for (edit::Commit::edit_iterator 223 I = commit.edit_begin(), E = commit.edit_end(); I != E; ++I) { 224 const edit::Commit::Edit &edit = *I; 225 switch (edit.Kind) { 226 case edit::Commit::Act_Insert: 227 commitInsert(edit.OrigLoc, edit.Offset, edit.Text, edit.BeforePrev); 228 break; 229 case edit::Commit::Act_InsertFromRange: 230 commitInsertFromRange(edit.OrigLoc, edit.Offset, 231 edit.InsertFromRangeOffs, edit.Length, 232 edit.BeforePrev); 233 break; 234 case edit::Commit::Act_Remove: 235 commitRemove(edit.OrigLoc, edit.Offset, edit.Length); 236 break; 237 } 238 } 239 240 return true; 241} 242 243// \brief Returns true if it is ok to make the two given characters adjacent. 244static bool canBeJoined(char left, char right, const LangOptions &LangOpts) { 245 // FIXME: Should use TokenConcatenation to make sure we don't allow stuff like 246 // making two '<' adjacent. 247 return !(Lexer::isIdentifierBodyChar(left, LangOpts) && 248 Lexer::isIdentifierBodyChar(right, LangOpts)); 249} 250 251/// \brief Returns true if it is ok to eliminate the trailing whitespace between 252/// the given characters. 253static bool canRemoveWhitespace(char left, char beforeWSpace, char right, 254 const LangOptions &LangOpts) { 255 if (!canBeJoined(left, right, LangOpts)) 256 return false; 257 if (isWhitespace(left) || isWhitespace(right)) 258 return true; 259 if (canBeJoined(beforeWSpace, right, LangOpts)) 260 return false; // the whitespace was intentional, keep it. 261 return true; 262} 263 264/// \brief Check the range that we are going to remove and: 265/// -Remove any trailing whitespace if possible. 266/// -Insert a space if removing the range is going to mess up the source tokens. 267static void adjustRemoval(const SourceManager &SM, const LangOptions &LangOpts, 268 SourceLocation Loc, FileOffset offs, 269 unsigned &len, StringRef &text) { 270 assert(len && text.empty()); 271 SourceLocation BeginTokLoc = Lexer::GetBeginningOfToken(Loc, SM, LangOpts); 272 if (BeginTokLoc != Loc) 273 return; // the range is not at the beginning of a token, keep the range. 274 275 bool Invalid = false; 276 StringRef buffer = SM.getBufferData(offs.getFID(), &Invalid); 277 if (Invalid) 278 return; 279 280 unsigned begin = offs.getOffset(); 281 unsigned end = begin + len; 282 283 // FIXME: Remove newline. 284 285 if (begin == 0) { 286 if (buffer[end] == ' ') 287 ++len; 288 return; 289 } 290 291 if (buffer[end] == ' ') { 292 if (canRemoveWhitespace(/*left=*/buffer[begin-1], 293 /*beforeWSpace=*/buffer[end-1], 294 /*right=*/buffer[end+1], 295 LangOpts)) 296 ++len; 297 return; 298 } 299 300 if (!canBeJoined(buffer[begin-1], buffer[end], LangOpts)) 301 text = " "; 302} 303 304static void applyRewrite(EditsReceiver &receiver, 305 StringRef text, FileOffset offs, unsigned len, 306 const SourceManager &SM, const LangOptions &LangOpts) { 307 assert(!offs.getFID().isInvalid()); 308 SourceLocation Loc = SM.getLocForStartOfFile(offs.getFID()); 309 Loc = Loc.getLocWithOffset(offs.getOffset()); 310 assert(Loc.isFileID()); 311 312 if (text.empty()) 313 adjustRemoval(SM, LangOpts, Loc, offs, len, text); 314 315 CharSourceRange range = CharSourceRange::getCharRange(Loc, 316 Loc.getLocWithOffset(len)); 317 318 if (text.empty()) { 319 assert(len); 320 receiver.remove(range); 321 return; 322 } 323 324 if (len) 325 receiver.replace(range, text); 326 else 327 receiver.insert(Loc, text); 328} 329 330void EditedSource::applyRewrites(EditsReceiver &receiver) { 331 SmallString<128> StrVec; 332 FileOffset CurOffs, CurEnd; 333 unsigned CurLen; 334 335 if (FileEdits.empty()) 336 return; 337 338 FileEditsTy::iterator I = FileEdits.begin(); 339 CurOffs = I->first; 340 StrVec = I->second.Text; 341 CurLen = I->second.RemoveLen; 342 CurEnd = CurOffs.getWithOffset(CurLen); 343 ++I; 344 345 for (FileEditsTy::iterator E = FileEdits.end(); I != E; ++I) { 346 FileOffset offs = I->first; 347 FileEdit act = I->second; 348 assert(offs >= CurEnd); 349 350 if (offs == CurEnd) { 351 StrVec += act.Text; 352 CurLen += act.RemoveLen; 353 CurEnd.getWithOffset(act.RemoveLen); 354 continue; 355 } 356 357 applyRewrite(receiver, StrVec.str(), CurOffs, CurLen, SourceMgr, LangOpts); 358 CurOffs = offs; 359 StrVec = act.Text; 360 CurLen = act.RemoveLen; 361 CurEnd = CurOffs.getWithOffset(CurLen); 362 } 363 364 applyRewrite(receiver, StrVec.str(), CurOffs, CurLen, SourceMgr, LangOpts); 365} 366 367void EditedSource::clearRewrites() { 368 FileEdits.clear(); 369 StrAlloc.Reset(); 370} 371 372StringRef EditedSource::getSourceText(FileOffset BeginOffs, FileOffset EndOffs, 373 bool &Invalid) { 374 assert(BeginOffs.getFID() == EndOffs.getFID()); 375 assert(BeginOffs <= EndOffs); 376 SourceLocation BLoc = SourceMgr.getLocForStartOfFile(BeginOffs.getFID()); 377 BLoc = BLoc.getLocWithOffset(BeginOffs.getOffset()); 378 assert(BLoc.isFileID()); 379 SourceLocation 380 ELoc = BLoc.getLocWithOffset(EndOffs.getOffset() - BeginOffs.getOffset()); 381 return Lexer::getSourceText(CharSourceRange::getCharRange(BLoc, ELoc), 382 SourceMgr, LangOpts, &Invalid); 383} 384 385EditedSource::FileEditsTy::iterator 386EditedSource::getActionForOffset(FileOffset Offs) { 387 FileEditsTy::iterator I = FileEdits.upper_bound(Offs); 388 if (I == FileEdits.begin()) 389 return FileEdits.end(); 390 --I; 391 FileEdit &FA = I->second; 392 FileOffset B = I->first; 393 FileOffset E = B.getWithOffset(FA.RemoveLen); 394 if (Offs >= B && Offs < E) 395 return I; 396 397 return FileEdits.end(); 398} 399