EditedSource.cpp revision 55fc873017f10f6f566b182b70f6fc22aefa3464
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/Basic/SourceManager.h" 12#include "clang/Edit/Commit.h" 13#include "clang/Edit/EditsReceiver.h" 14#include "clang/Lex/Lexer.h" 15#include "llvm/ADT/SmallString.h" 16#include "llvm/ADT/Twine.h" 17 18using namespace clang; 19using namespace edit; 20 21void EditsReceiver::remove(CharSourceRange range) { 22 replace(range, StringRef()); 23} 24 25StringRef EditedSource::copyString(const Twine &twine) { 26 llvm::SmallString<128> Data; 27 return copyString(twine.toStringRef(Data)); 28} 29 30bool 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 52bool 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 84bool 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 == B) 104 break; 105 106 if (BeginOffs < E) { 107 if (BeginOffs > B) { 108 BeginOffs = E; 109 ++I; 110 } 111 break; 112 } 113 } 114 115 for (; I != FileEdits.end() && EndOffs > I->first; ++I) { 116 FileEdit &FA = I->second; 117 FileOffset B = I->first; 118 FileOffset E = B.getWithOffset(FA.RemoveLen); 119 120 if (BeginOffs < B) { 121 bool Invalid = false; 122 StringRef text = getSourceText(BeginOffs, B, Invalid); 123 if (Invalid) 124 return false; 125 StrVec += text; 126 } 127 StrVec += FA.Text; 128 BeginOffs = E; 129 } 130 131 if (BeginOffs < EndOffs) { 132 bool Invalid = false; 133 StringRef text = getSourceText(BeginOffs, EndOffs, Invalid); 134 if (Invalid) 135 return false; 136 StrVec += text; 137 } 138 139 return commitInsert(OrigLoc, Offs, StrVec.str(), beforePreviousInsertions); 140} 141 142void EditedSource::commitRemove(SourceLocation OrigLoc, 143 FileOffset BeginOffs, unsigned Len) { 144 if (Len == 0) 145 return; 146 147 FileOffset EndOffs = BeginOffs.getWithOffset(Len); 148 FileEditsTy::iterator I = FileEdits.upper_bound(BeginOffs); 149 if (I != FileEdits.begin()) 150 --I; 151 152 for (; I != FileEdits.end(); ++I) { 153 FileEdit &FA = I->second; 154 FileOffset B = I->first; 155 FileOffset E = B.getWithOffset(FA.RemoveLen); 156 157 if (BeginOffs < E) 158 break; 159 } 160 161 FileOffset TopBegin, TopEnd; 162 FileEdit *TopFA = 0; 163 164 if (I == FileEdits.end()) { 165 FileEditsTy::iterator 166 NewI = FileEdits.insert(I, std::make_pair(BeginOffs, FileEdit())); 167 NewI->second.RemoveLen = Len; 168 return; 169 } 170 171 FileEdit &FA = I->second; 172 FileOffset B = I->first; 173 FileOffset E = B.getWithOffset(FA.RemoveLen); 174 if (BeginOffs < B) { 175 FileEditsTy::iterator 176 NewI = FileEdits.insert(I, std::make_pair(BeginOffs, FileEdit())); 177 TopBegin = BeginOffs; 178 TopEnd = EndOffs; 179 TopFA = &NewI->second; 180 TopFA->RemoveLen = Len; 181 } else { 182 TopBegin = B; 183 TopEnd = E; 184 TopFA = &I->second; 185 if (TopEnd >= EndOffs) 186 return; 187 unsigned diff = EndOffs.getOffset() - TopEnd.getOffset(); 188 TopEnd = EndOffs; 189 TopFA->RemoveLen += diff; 190 ++I; 191 } 192 193 while (I != FileEdits.end()) { 194 FileEdit &FA = I->second; 195 FileOffset B = I->first; 196 FileOffset E = B.getWithOffset(FA.RemoveLen); 197 198 if (B >= TopEnd) 199 break; 200 201 if (E <= TopEnd) { 202 FileEdits.erase(I++); 203 continue; 204 } 205 206 if (B < TopEnd) { 207 unsigned diff = E.getOffset() - TopEnd.getOffset(); 208 TopEnd = E; 209 TopFA->RemoveLen += diff; 210 FileEdits.erase(I); 211 } 212 213 break; 214 } 215} 216 217bool EditedSource::commit(const Commit &commit) { 218 if (!commit.isCommitable()) 219 return false; 220 221 for (edit::Commit::edit_iterator 222 I = commit.edit_begin(), E = commit.edit_end(); I != E; ++I) { 223 const edit::Commit::Edit &edit = *I; 224 switch (edit.Kind) { 225 case edit::Commit::Act_Insert: 226 commitInsert(edit.OrigLoc, edit.Offset, edit.Text, edit.BeforePrev); 227 break; 228 case edit::Commit::Act_InsertFromRange: 229 commitInsertFromRange(edit.OrigLoc, edit.Offset, 230 edit.InsertFromRangeOffs, edit.Length, 231 edit.BeforePrev); 232 break; 233 case edit::Commit::Act_Remove: 234 commitRemove(edit.OrigLoc, edit.Offset, edit.Length); 235 break; 236 } 237 } 238 239 return true; 240} 241 242static void applyRewrite(EditsReceiver &receiver, 243 StringRef text, FileOffset offs, unsigned len, 244 const SourceManager &SM) { 245 assert(!offs.getFID().isInvalid()); 246 SourceLocation Loc = SM.getLocForStartOfFile(offs.getFID()); 247 Loc = Loc.getLocWithOffset(offs.getOffset()); 248 assert(Loc.isFileID()); 249 CharSourceRange range = CharSourceRange::getCharRange(Loc, 250 Loc.getLocWithOffset(len)); 251 252 if (text.empty()) { 253 assert(len); 254 receiver.remove(range); 255 return; 256 } 257 258 if (len) 259 receiver.replace(range, text); 260 else 261 receiver.insert(Loc, text); 262} 263 264void EditedSource::applyRewrites(EditsReceiver &receiver) { 265 llvm::SmallString<128> StrVec; 266 FileOffset CurOffs, CurEnd; 267 unsigned CurLen; 268 269 if (FileEdits.empty()) 270 return; 271 272 FileEditsTy::iterator I = FileEdits.begin(); 273 CurOffs = I->first; 274 StrVec = I->second.Text; 275 CurLen = I->second.RemoveLen; 276 CurEnd = CurOffs.getWithOffset(CurLen); 277 ++I; 278 279 for (FileEditsTy::iterator E = FileEdits.end(); I != E; ++I) { 280 FileOffset offs = I->first; 281 FileEdit act = I->second; 282 assert(offs >= CurEnd); 283 284 if (offs == CurEnd) { 285 StrVec += act.Text; 286 CurLen += act.RemoveLen; 287 CurEnd.getWithOffset(act.RemoveLen); 288 continue; 289 } 290 291 applyRewrite(receiver, StrVec.str(), CurOffs, CurLen, SourceMgr); 292 CurOffs = offs; 293 StrVec = act.Text; 294 CurLen = act.RemoveLen; 295 CurEnd = CurOffs.getWithOffset(CurLen); 296 } 297 298 applyRewrite(receiver, StrVec.str(), CurOffs, CurLen, SourceMgr); 299} 300 301void EditedSource::clearRewrites() { 302 FileEdits.clear(); 303 StrAlloc.Reset(); 304} 305 306StringRef EditedSource::getSourceText(FileOffset BeginOffs, FileOffset EndOffs, 307 bool &Invalid) { 308 assert(BeginOffs.getFID() == EndOffs.getFID()); 309 assert(BeginOffs <= EndOffs); 310 SourceLocation BLoc = SourceMgr.getLocForStartOfFile(BeginOffs.getFID()); 311 BLoc = BLoc.getLocWithOffset(BeginOffs.getOffset()); 312 assert(BLoc.isFileID()); 313 SourceLocation 314 ELoc = BLoc.getLocWithOffset(EndOffs.getOffset() - BeginOffs.getOffset()); 315 return Lexer::getSourceText(CharSourceRange::getCharRange(BLoc, ELoc), 316 SourceMgr, LangOpts, &Invalid); 317} 318 319EditedSource::FileEditsTy::iterator 320EditedSource::getActionForOffset(FileOffset Offs) { 321 FileEditsTy::iterator I = FileEdits.upper_bound(Offs); 322 if (I == FileEdits.begin()) 323 return FileEdits.end(); 324 --I; 325 FileEdit &FA = I->second; 326 FileOffset B = I->first; 327 FileOffset E = B.getWithOffset(FA.RemoveLen); 328 if (Offs >= B && Offs < E) 329 return I; 330 331 return FileEdits.end(); 332} 333