1 //===----- EditedSource.cpp - Collection of source 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/EditedSource.h" 11 #include "clang/Edit/Commit.h" 12 #include "clang/Edit/EditsReceiver.h" 13 #include "clang/Lex/Lexer.h" 14 #include "clang/Basic/SourceManager.h" 15 #include "llvm/ADT/SmallString.h" 16 #include "llvm/ADT/Twine.h" 17 18 using namespace clang; 19 using namespace edit; 20 21 void EditsReceiver::remove(CharSourceRange range) { 22 replace(range, StringRef()); 23 } 24 25 StringRef EditedSource::copyString(const Twine &twine) { 26 llvm::SmallString<128> Data; 27 return copyString(twine.toStringRef(Data)); 28 } 29 30 bool EditedSource::canInsertInOffset(SourceLocation OrigLoc, FileOffset Offs) { 31 FileEditsTy::iterator FA = getActionForOffset(Offs); 32 if (FA != FileEdits.end()) { 33 if (FA->first != Offs) 34 return false; // position has been removed. 35 } 36 37 if (SourceMgr.isMacroArgExpansion(OrigLoc)) { 38 SourceLocation 39 DefArgLoc = SourceMgr.getImmediateExpansionRange(OrigLoc).first; 40 SourceLocation 41 ExpLoc = SourceMgr.getImmediateExpansionRange(DefArgLoc).first; 42 llvm::DenseMap<unsigned, SourceLocation>::iterator 43 I = ExpansionToArgMap.find(ExpLoc.getRawEncoding()); 44 if (I != ExpansionToArgMap.end() && I->second != DefArgLoc) 45 return false; // Trying to write in a macro argument input that has 46 // already been written for another argument of the same macro. 47 } 48 49 return true; 50 } 51 52 bool EditedSource::commitInsert(SourceLocation OrigLoc, 53 FileOffset Offs, StringRef text, 54 bool beforePreviousInsertions) { 55 if (!canInsertInOffset(OrigLoc, Offs)) 56 return false; 57 if (text.empty()) 58 return true; 59 60 if (SourceMgr.isMacroArgExpansion(OrigLoc)) { 61 SourceLocation 62 DefArgLoc = SourceMgr.getImmediateExpansionRange(OrigLoc).first; 63 SourceLocation 64 ExpLoc = SourceMgr.getImmediateExpansionRange(DefArgLoc).first; 65 ExpansionToArgMap[ExpLoc.getRawEncoding()] = DefArgLoc; 66 } 67 68 FileEdit &FA = FileEdits[Offs]; 69 if (FA.Text.empty()) { 70 FA.Text = copyString(text); 71 return true; 72 } 73 74 Twine concat; 75 if (beforePreviousInsertions) 76 concat = Twine(text) + FA.Text; 77 else 78 concat = Twine(FA.Text) + text; 79 80 FA.Text = copyString(concat); 81 return true; 82 } 83 84 bool EditedSource::commitInsertFromRange(SourceLocation OrigLoc, 85 FileOffset Offs, 86 FileOffset InsertFromRangeOffs, unsigned Len, 87 bool beforePreviousInsertions) { 88 if (Len == 0) 89 return true; 90 91 llvm::SmallString<128> StrVec; 92 FileOffset BeginOffs = InsertFromRangeOffs; 93 FileOffset EndOffs = BeginOffs.getWithOffset(Len); 94 FileEditsTy::iterator I = FileEdits.upper_bound(BeginOffs); 95 if (I != FileEdits.begin()) 96 --I; 97 98 for (; I != FileEdits.end(); ++I) { 99 FileEdit &FA = I->second; 100 FileOffset B = I->first; 101 FileOffset E = B.getWithOffset(FA.RemoveLen); 102 103 if (BeginOffs < E) { 104 if (BeginOffs >= B) { 105 BeginOffs = E; 106 ++I; 107 } 108 break; 109 } 110 } 111 112 for (; I != FileEdits.end() && EndOffs > I->first; ++I) { 113 FileEdit &FA = I->second; 114 FileOffset B = I->first; 115 FileOffset E = B.getWithOffset(FA.RemoveLen); 116 117 if (BeginOffs < B) { 118 bool Invalid = false; 119 StringRef text = getSourceText(BeginOffs, B, Invalid); 120 if (Invalid) 121 return false; 122 StrVec += text; 123 } 124 StrVec += FA.Text; 125 BeginOffs = E; 126 } 127 128 if (BeginOffs < EndOffs) { 129 bool Invalid = false; 130 StringRef text = getSourceText(BeginOffs, EndOffs, Invalid); 131 if (Invalid) 132 return false; 133 StrVec += text; 134 } 135 136 return commitInsert(OrigLoc, Offs, StrVec.str(), beforePreviousInsertions); 137 } 138 139 void EditedSource::commitRemove(SourceLocation OrigLoc, 140 FileOffset BeginOffs, unsigned Len) { 141 if (Len == 0) 142 return; 143 144 FileOffset EndOffs = BeginOffs.getWithOffset(Len); 145 FileEditsTy::iterator I = FileEdits.upper_bound(BeginOffs); 146 if (I != FileEdits.begin()) 147 --I; 148 149 for (; I != FileEdits.end(); ++I) { 150 FileEdit &FA = I->second; 151 FileOffset B = I->first; 152 FileOffset E = B.getWithOffset(FA.RemoveLen); 153 154 if (BeginOffs < E) 155 break; 156 } 157 158 FileOffset TopBegin, TopEnd; 159 FileEdit *TopFA = 0; 160 161 if (I == FileEdits.end()) { 162 FileEditsTy::iterator 163 NewI = FileEdits.insert(I, std::make_pair(BeginOffs, FileEdit())); 164 NewI->second.RemoveLen = Len; 165 return; 166 } 167 168 FileEdit &FA = I->second; 169 FileOffset B = I->first; 170 FileOffset E = B.getWithOffset(FA.RemoveLen); 171 if (BeginOffs < B) { 172 FileEditsTy::iterator 173 NewI = FileEdits.insert(I, std::make_pair(BeginOffs, FileEdit())); 174 TopBegin = BeginOffs; 175 TopEnd = EndOffs; 176 TopFA = &NewI->second; 177 TopFA->RemoveLen = Len; 178 } else { 179 TopBegin = B; 180 TopEnd = E; 181 TopFA = &I->second; 182 if (TopEnd >= EndOffs) 183 return; 184 unsigned diff = EndOffs.getOffset() - TopEnd.getOffset(); 185 TopEnd = EndOffs; 186 TopFA->RemoveLen += diff; 187 ++I; 188 } 189 190 while (I != FileEdits.end()) { 191 FileEdit &FA = I->second; 192 FileOffset B = I->first; 193 FileOffset E = B.getWithOffset(FA.RemoveLen); 194 195 if (B >= TopEnd) 196 break; 197 198 if (E <= TopEnd) { 199 FileEdits.erase(I++); 200 continue; 201 } 202 203 if (B < TopEnd) { 204 unsigned diff = E.getOffset() - TopEnd.getOffset(); 205 TopEnd = E; 206 TopFA->RemoveLen += diff; 207 FileEdits.erase(I); 208 } 209 210 break; 211 } 212 } 213 214 bool EditedSource::commit(const Commit &commit) { 215 if (!commit.isCommitable()) 216 return false; 217 218 for (edit::Commit::edit_iterator 219 I = commit.edit_begin(), E = commit.edit_end(); I != E; ++I) { 220 const edit::Commit::Edit &edit = *I; 221 switch (edit.Kind) { 222 case edit::Commit::Act_Insert: 223 commitInsert(edit.OrigLoc, edit.Offset, edit.Text, edit.BeforePrev); 224 break; 225 case edit::Commit::Act_InsertFromRange: 226 commitInsertFromRange(edit.OrigLoc, edit.Offset, 227 edit.InsertFromRangeOffs, edit.Length, 228 edit.BeforePrev); 229 break; 230 case edit::Commit::Act_Remove: 231 commitRemove(edit.OrigLoc, edit.Offset, edit.Length); 232 break; 233 } 234 } 235 236 return true; 237 } 238 239 static void applyRewrite(EditsReceiver &receiver, 240 StringRef text, FileOffset offs, unsigned len, 241 const SourceManager &SM) { 242 assert(!offs.getFID().isInvalid()); 243 SourceLocation Loc = SM.getLocForStartOfFile(offs.getFID()); 244 Loc = Loc.getLocWithOffset(offs.getOffset()); 245 assert(Loc.isFileID()); 246 CharSourceRange range = CharSourceRange::getCharRange(Loc, 247 Loc.getLocWithOffset(len)); 248 249 if (text.empty()) { 250 assert(len); 251 receiver.remove(range); 252 return; 253 } 254 255 if (len) 256 receiver.replace(range, text); 257 else 258 receiver.insert(Loc, text); 259 } 260 261 void EditedSource::applyRewrites(EditsReceiver &receiver) { 262 llvm::SmallString<128> StrVec; 263 FileOffset CurOffs, CurEnd; 264 unsigned CurLen; 265 266 if (FileEdits.empty()) 267 return; 268 269 FileEditsTy::iterator I = FileEdits.begin(); 270 CurOffs = I->first; 271 StrVec = I->second.Text; 272 CurLen = I->second.RemoveLen; 273 CurEnd = CurOffs.getWithOffset(CurLen); 274 ++I; 275 276 for (FileEditsTy::iterator E = FileEdits.end(); I != E; ++I) { 277 FileOffset offs = I->first; 278 FileEdit act = I->second; 279 assert(offs >= CurEnd); 280 281 if (offs == CurEnd) { 282 StrVec += act.Text; 283 CurLen += act.RemoveLen; 284 CurEnd.getWithOffset(act.RemoveLen); 285 continue; 286 } 287 288 applyRewrite(receiver, StrVec.str(), CurOffs, CurLen, SourceMgr); 289 CurOffs = offs; 290 StrVec = act.Text; 291 CurLen = act.RemoveLen; 292 CurEnd = CurOffs.getWithOffset(CurLen); 293 } 294 295 applyRewrite(receiver, StrVec.str(), CurOffs, CurLen, SourceMgr); 296 } 297 298 void EditedSource::clearRewrites() { 299 FileEdits.clear(); 300 StrAlloc.Reset(); 301 } 302 303 StringRef EditedSource::getSourceText(FileOffset BeginOffs, FileOffset EndOffs, 304 bool &Invalid) { 305 assert(BeginOffs.getFID() == EndOffs.getFID()); 306 assert(BeginOffs <= EndOffs); 307 SourceLocation BLoc = SourceMgr.getLocForStartOfFile(BeginOffs.getFID()); 308 BLoc = BLoc.getLocWithOffset(BeginOffs.getOffset()); 309 assert(BLoc.isFileID()); 310 SourceLocation 311 ELoc = BLoc.getLocWithOffset(EndOffs.getOffset() - BeginOffs.getOffset()); 312 return Lexer::getSourceText(CharSourceRange::getCharRange(BLoc, ELoc), 313 SourceMgr, LangOpts, &Invalid); 314 } 315 316 EditedSource::FileEditsTy::iterator 317 EditedSource::getActionForOffset(FileOffset Offs) { 318 FileEditsTy::iterator I = FileEdits.upper_bound(Offs); 319 if (I == FileEdits.begin()) 320 return FileEdits.end(); 321 --I; 322 FileEdit &FA = I->second; 323 FileOffset B = I->first; 324 FileOffset E = B.getWithOffset(FA.RemoveLen); 325 if (Offs >= B && Offs < E) 326 return I; 327 328 return FileEdits.end(); 329 } 330