1 //===----- EditedSource.cpp - Collection of source edits ------------------===//
3 // The LLVM Compiler Infrastructure
5 // This file is distributed under the University of Illinois Open Source
6 // License. See LICENSE.TXT for details.
8 //===----------------------------------------------------------------------===//
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"
18 using namespace clang;
21 void EditsReceiver::remove(CharSourceRange range) {
22 replace(range, StringRef());
25 StringRef EditedSource::copyString(const Twine &twine) {
26 llvm::SmallString<128> Data;
27 return copyString(twine.toStringRef(Data));
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.
37 if (SourceMgr.isMacroArgExpansion(OrigLoc)) {
39 DefArgLoc = SourceMgr.getImmediateExpansionRange(OrigLoc).first;
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.
52 bool EditedSource::commitInsert(SourceLocation OrigLoc,
53 FileOffset Offs, StringRef text,
54 bool beforePreviousInsertions) {
55 if (!canInsertInOffset(OrigLoc, Offs))
60 if (SourceMgr.isMacroArgExpansion(OrigLoc)) {
62 DefArgLoc = SourceMgr.getImmediateExpansionRange(OrigLoc).first;
64 ExpLoc = SourceMgr.getImmediateExpansionRange(DefArgLoc).first;
65 ExpansionToArgMap[ExpLoc.getRawEncoding()] = DefArgLoc;
68 FileEdit &FA = FileEdits[Offs];
69 if (FA.Text.empty()) {
70 FA.Text = copyString(text);
75 if (beforePreviousInsertions)
76 concat = Twine(text) + FA.Text;
78 concat = Twine(FA.Text) + text;
80 FA.Text = copyString(concat);
84 bool EditedSource::commitInsertFromRange(SourceLocation OrigLoc,
86 FileOffset InsertFromRangeOffs, unsigned Len,
87 bool beforePreviousInsertions) {
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())
98 for (; I != FileEdits.end(); ++I) {
99 FileEdit &FA = I->second;
100 FileOffset B = I->first;
101 FileOffset E = B.getWithOffset(FA.RemoveLen);
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);
121 bool Invalid = false;
122 StringRef text = getSourceText(BeginOffs, B, Invalid);
131 if (BeginOffs < EndOffs) {
132 bool Invalid = false;
133 StringRef text = getSourceText(BeginOffs, EndOffs, Invalid);
139 return commitInsert(OrigLoc, Offs, StrVec.str(), beforePreviousInsertions);
142 void EditedSource::commitRemove(SourceLocation OrigLoc,
143 FileOffset BeginOffs, unsigned Len) {
147 FileOffset EndOffs = BeginOffs.getWithOffset(Len);
148 FileEditsTy::iterator I = FileEdits.upper_bound(BeginOffs);
149 if (I != FileEdits.begin())
152 for (; I != FileEdits.end(); ++I) {
153 FileEdit &FA = I->second;
154 FileOffset B = I->first;
155 FileOffset E = B.getWithOffset(FA.RemoveLen);
161 FileOffset TopBegin, TopEnd;
164 if (I == FileEdits.end()) {
165 FileEditsTy::iterator
166 NewI = FileEdits.insert(I, std::make_pair(BeginOffs, FileEdit()));
167 NewI->second.RemoveLen = Len;
171 FileEdit &FA = I->second;
172 FileOffset B = I->first;
173 FileOffset E = B.getWithOffset(FA.RemoveLen);
175 FileEditsTy::iterator
176 NewI = FileEdits.insert(I, std::make_pair(BeginOffs, FileEdit()));
177 TopBegin = BeginOffs;
179 TopFA = &NewI->second;
180 TopFA->RemoveLen = Len;
185 if (TopEnd >= EndOffs)
187 unsigned diff = EndOffs.getOffset() - TopEnd.getOffset();
189 TopFA->RemoveLen += diff;
193 while (I != FileEdits.end()) {
194 FileEdit &FA = I->second;
195 FileOffset B = I->first;
196 FileOffset E = B.getWithOffset(FA.RemoveLen);
202 FileEdits.erase(I++);
207 unsigned diff = E.getOffset() - TopEnd.getOffset();
209 TopFA->RemoveLen += diff;
217 bool EditedSource::commit(const Commit &commit) {
218 if (!commit.isCommitable())
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;
225 case edit::Commit::Act_Insert:
226 commitInsert(edit.OrigLoc, edit.Offset, edit.Text, edit.BeforePrev);
228 case edit::Commit::Act_InsertFromRange:
229 commitInsertFromRange(edit.OrigLoc, edit.Offset,
230 edit.InsertFromRangeOffs, edit.Length,
233 case edit::Commit::Act_Remove:
234 commitRemove(edit.OrigLoc, edit.Offset, edit.Length);
242 static 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));
254 receiver.remove(range);
259 receiver.replace(range, text);
261 receiver.insert(Loc, text);
264 void EditedSource::applyRewrites(EditsReceiver &receiver) {
265 llvm::SmallString<128> StrVec;
266 FileOffset CurOffs, CurEnd;
269 if (FileEdits.empty())
272 FileEditsTy::iterator I = FileEdits.begin();
274 StrVec = I->second.Text;
275 CurLen = I->second.RemoveLen;
276 CurEnd = CurOffs.getWithOffset(CurLen);
279 for (FileEditsTy::iterator E = FileEdits.end(); I != E; ++I) {
280 FileOffset offs = I->first;
281 FileEdit act = I->second;
282 assert(offs >= CurEnd);
284 if (offs == CurEnd) {
286 CurLen += act.RemoveLen;
287 CurEnd.getWithOffset(act.RemoveLen);
291 applyRewrite(receiver, StrVec.str(), CurOffs, CurLen, SourceMgr);
294 CurLen = act.RemoveLen;
295 CurEnd = CurOffs.getWithOffset(CurLen);
298 applyRewrite(receiver, StrVec.str(), CurOffs, CurLen, SourceMgr);
301 void EditedSource::clearRewrites() {
306 StringRef EditedSource::getSourceText(FileOffset BeginOffs, FileOffset EndOffs,
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());
314 ELoc = BLoc.getLocWithOffset(EndOffs.getOffset() - BeginOffs.getOffset());
315 return Lexer::getSourceText(CharSourceRange::getCharRange(BLoc, ELoc),
316 SourceMgr, LangOpts, &Invalid);
319 EditedSource::FileEditsTy::iterator
320 EditedSource::getActionForOffset(FileOffset Offs) {
321 FileEditsTy::iterator I = FileEdits.upper_bound(Offs);
322 if (I == FileEdits.begin())
323 return FileEdits.end();
325 FileEdit &FA = I->second;
326 FileOffset B = I->first;
327 FileOffset E = B.getWithOffset(FA.RemoveLen);
328 if (Offs >= B && Offs < E)
331 return FileEdits.end();