]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - contrib/llvm/tools/clang/lib/Edit/EditedSource.cpp
dts: Update our device tree sources file fomr Linux 4.13
[FreeBSD/FreeBSD.git] / contrib / llvm / tools / clang / lib / Edit / EditedSource.cpp
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/CharInfo.h"
12 #include "clang/Basic/SourceManager.h"
13 #include "clang/Edit/Commit.h"
14 #include "clang/Edit/EditsReceiver.h"
15 #include "clang/Lex/Lexer.h"
16 #include "llvm/ADT/SmallString.h"
17 #include "llvm/ADT/Twine.h"
18
19 using namespace clang;
20 using namespace edit;
21
22 void EditsReceiver::remove(CharSourceRange range) {
23   replace(range, StringRef());
24 }
25
26 void EditedSource::deconstructMacroArgLoc(SourceLocation Loc,
27                                           SourceLocation &ExpansionLoc,
28                                           MacroArgUse &ArgUse) {
29   assert(SourceMgr.isMacroArgExpansion(Loc));
30   SourceLocation DefArgLoc = SourceMgr.getImmediateExpansionRange(Loc).first;
31   SourceLocation ImmediateExpansionLoc =
32       SourceMgr.getImmediateExpansionRange(DefArgLoc).first;
33   ExpansionLoc = ImmediateExpansionLoc;
34   while (SourceMgr.isMacroBodyExpansion(ExpansionLoc))
35     ExpansionLoc = SourceMgr.getImmediateExpansionRange(ExpansionLoc).first;
36   SmallString<20> Buf;
37   StringRef ArgName = Lexer::getSpelling(SourceMgr.getSpellingLoc(DefArgLoc),
38                                          Buf, SourceMgr, LangOpts);
39   ArgUse = MacroArgUse{nullptr, SourceLocation(), SourceLocation()};
40   if (!ArgName.empty())
41     ArgUse = {&IdentTable.get(ArgName), ImmediateExpansionLoc,
42               SourceMgr.getSpellingLoc(DefArgLoc)};
43 }
44
45 void EditedSource::startingCommit() {}
46
47 void EditedSource::finishedCommit() {
48   for (auto &ExpArg : CurrCommitMacroArgExps) {
49     SourceLocation ExpLoc;
50     MacroArgUse ArgUse;
51     std::tie(ExpLoc, ArgUse) = ExpArg;
52     auto &ArgUses = ExpansionToArgMap[ExpLoc.getRawEncoding()];
53     if (std::find(ArgUses.begin(), ArgUses.end(), ArgUse) == ArgUses.end())
54       ArgUses.push_back(ArgUse);
55   }
56   CurrCommitMacroArgExps.clear();
57 }
58
59 StringRef EditedSource::copyString(const Twine &twine) {
60   SmallString<128> Data;
61   return copyString(twine.toStringRef(Data));
62 }
63
64 bool EditedSource::canInsertInOffset(SourceLocation OrigLoc, FileOffset Offs) {
65   FileEditsTy::iterator FA = getActionForOffset(Offs);
66   if (FA != FileEdits.end()) {
67     if (FA->first != Offs)
68       return false; // position has been removed.
69   }
70
71   if (SourceMgr.isMacroArgExpansion(OrigLoc)) {
72     SourceLocation ExpLoc;
73     MacroArgUse ArgUse;
74     deconstructMacroArgLoc(OrigLoc, ExpLoc, ArgUse);
75     auto I = ExpansionToArgMap.find(ExpLoc.getRawEncoding());
76     if (I != ExpansionToArgMap.end() &&
77         find_if(I->second, [&](const MacroArgUse &U) {
78           return ArgUse.Identifier == U.Identifier &&
79                  std::tie(ArgUse.ImmediateExpansionLoc, ArgUse.UseLoc) !=
80                      std::tie(U.ImmediateExpansionLoc, U.UseLoc);
81         }) != I->second.end()) {
82       // Trying to write in a macro argument input that has already been
83       // written by a previous commit for another expansion of the same macro
84       // argument name. For example:
85       //
86       // \code
87       //   #define MAC(x) ((x)+(x))
88       //   MAC(a)
89       // \endcode
90       //
91       // A commit modified the macro argument 'a' due to the first '(x)'
92       // expansion inside the macro definition, and a subsequent commit tried
93       // to modify 'a' again for the second '(x)' expansion. The edits of the
94       // second commit will be rejected.
95       return false;
96     }
97   }
98   return true;
99 }
100
101 bool EditedSource::commitInsert(SourceLocation OrigLoc,
102                                 FileOffset Offs, StringRef text,
103                                 bool beforePreviousInsertions) {
104   if (!canInsertInOffset(OrigLoc, Offs))
105     return false;
106   if (text.empty())
107     return true;
108
109   if (SourceMgr.isMacroArgExpansion(OrigLoc)) {
110     MacroArgUse ArgUse;
111     SourceLocation ExpLoc;
112     deconstructMacroArgLoc(OrigLoc, ExpLoc, ArgUse);
113     if (ArgUse.Identifier)
114       CurrCommitMacroArgExps.emplace_back(ExpLoc, ArgUse);
115   }
116
117   FileEdit &FA = FileEdits[Offs];
118   if (FA.Text.empty()) {
119     FA.Text = copyString(text);
120     return true;
121   }
122
123   if (beforePreviousInsertions)
124     FA.Text = copyString(Twine(text) + FA.Text);
125   else
126     FA.Text = copyString(Twine(FA.Text) + text);
127
128   return true;
129 }
130
131 bool EditedSource::commitInsertFromRange(SourceLocation OrigLoc,
132                                    FileOffset Offs,
133                                    FileOffset InsertFromRangeOffs, unsigned Len,
134                                    bool beforePreviousInsertions) {
135   if (Len == 0)
136     return true;
137
138   SmallString<128> StrVec;
139   FileOffset BeginOffs = InsertFromRangeOffs;
140   FileOffset EndOffs = BeginOffs.getWithOffset(Len);
141   FileEditsTy::iterator I = FileEdits.upper_bound(BeginOffs);
142   if (I != FileEdits.begin())
143     --I;
144
145   for (; I != FileEdits.end(); ++I) {
146     FileEdit &FA = I->second;
147     FileOffset B = I->first;
148     FileOffset E = B.getWithOffset(FA.RemoveLen);
149
150     if (BeginOffs == B)
151       break;
152
153     if (BeginOffs < E) {
154       if (BeginOffs > B) {
155         BeginOffs = E;
156         ++I;
157       }
158       break;
159     }
160   }
161
162   for (; I != FileEdits.end() && EndOffs > I->first; ++I) {
163     FileEdit &FA = I->second;
164     FileOffset B = I->first;
165     FileOffset E = B.getWithOffset(FA.RemoveLen);
166
167     if (BeginOffs < B) {
168       bool Invalid = false;
169       StringRef text = getSourceText(BeginOffs, B, Invalid);
170       if (Invalid)
171         return false;
172       StrVec += text;
173     }
174     StrVec += FA.Text;
175     BeginOffs = E;
176   }
177
178   if (BeginOffs < EndOffs) {
179     bool Invalid = false;
180     StringRef text = getSourceText(BeginOffs, EndOffs, Invalid);
181     if (Invalid)
182       return false;
183     StrVec += text;
184   }
185
186   return commitInsert(OrigLoc, Offs, StrVec, beforePreviousInsertions);
187 }
188
189 void EditedSource::commitRemove(SourceLocation OrigLoc,
190                                 FileOffset BeginOffs, unsigned Len) {
191   if (Len == 0)
192     return;
193
194   FileOffset EndOffs = BeginOffs.getWithOffset(Len);
195   FileEditsTy::iterator I = FileEdits.upper_bound(BeginOffs);
196   if (I != FileEdits.begin())
197     --I;
198
199   for (; I != FileEdits.end(); ++I) {
200     FileEdit &FA = I->second;
201     FileOffset B = I->first;
202     FileOffset E = B.getWithOffset(FA.RemoveLen);
203
204     if (BeginOffs < E)
205       break;
206   }
207
208   FileOffset TopBegin, TopEnd;
209   FileEdit *TopFA = nullptr;
210
211   if (I == FileEdits.end()) {
212     FileEditsTy::iterator
213       NewI = FileEdits.insert(I, std::make_pair(BeginOffs, FileEdit()));
214     NewI->second.RemoveLen = Len;
215     return;
216   }
217
218   FileEdit &FA = I->second;
219   FileOffset B = I->first;
220   FileOffset E = B.getWithOffset(FA.RemoveLen);
221   if (BeginOffs < B) {
222     FileEditsTy::iterator
223       NewI = FileEdits.insert(I, std::make_pair(BeginOffs, FileEdit()));
224     TopBegin = BeginOffs;
225     TopEnd = EndOffs;
226     TopFA = &NewI->second;
227     TopFA->RemoveLen = Len;
228   } else {
229     TopBegin = B;
230     TopEnd = E;
231     TopFA = &I->second;
232     if (TopEnd >= EndOffs)
233       return;
234     unsigned diff = EndOffs.getOffset() - TopEnd.getOffset();
235     TopEnd = EndOffs;
236     TopFA->RemoveLen += diff;
237     if (B == BeginOffs)
238       TopFA->Text = StringRef();
239     ++I;
240   }
241
242   while (I != FileEdits.end()) {
243     FileEdit &FA = I->second;
244     FileOffset B = I->first;
245     FileOffset E = B.getWithOffset(FA.RemoveLen);
246
247     if (B >= TopEnd)
248       break;
249
250     if (E <= TopEnd) {
251       FileEdits.erase(I++);
252       continue;
253     }
254
255     if (B < TopEnd) {
256       unsigned diff = E.getOffset() - TopEnd.getOffset();
257       TopEnd = E;
258       TopFA->RemoveLen += diff;
259       FileEdits.erase(I);
260     }
261
262     break;
263   }
264 }
265
266 bool EditedSource::commit(const Commit &commit) {
267   if (!commit.isCommitable())
268     return false;
269
270   struct CommitRAII {
271     EditedSource &Editor;
272     CommitRAII(EditedSource &Editor) : Editor(Editor) {
273       Editor.startingCommit();
274     }
275     ~CommitRAII() {
276       Editor.finishedCommit();
277     }
278   } CommitRAII(*this);
279
280   for (edit::Commit::edit_iterator
281          I = commit.edit_begin(), E = commit.edit_end(); I != E; ++I) {
282     const edit::Commit::Edit &edit = *I;
283     switch (edit.Kind) {
284     case edit::Commit::Act_Insert:
285       commitInsert(edit.OrigLoc, edit.Offset, edit.Text, edit.BeforePrev);
286       break;
287     case edit::Commit::Act_InsertFromRange:
288       commitInsertFromRange(edit.OrigLoc, edit.Offset,
289                             edit.InsertFromRangeOffs, edit.Length,
290                             edit.BeforePrev);
291       break;
292     case edit::Commit::Act_Remove:
293       commitRemove(edit.OrigLoc, edit.Offset, edit.Length);
294       break;
295     }
296   }
297
298   return true;
299 }
300
301 // \brief Returns true if it is ok to make the two given characters adjacent.
302 static bool canBeJoined(char left, char right, const LangOptions &LangOpts) {
303   // FIXME: Should use TokenConcatenation to make sure we don't allow stuff like
304   // making two '<' adjacent.
305   return !(Lexer::isIdentifierBodyChar(left, LangOpts) &&
306            Lexer::isIdentifierBodyChar(right, LangOpts));
307 }
308
309 /// \brief Returns true if it is ok to eliminate the trailing whitespace between
310 /// the given characters.
311 static bool canRemoveWhitespace(char left, char beforeWSpace, char right,
312                                 const LangOptions &LangOpts) {
313   if (!canBeJoined(left, right, LangOpts))
314     return false;
315   if (isWhitespace(left) || isWhitespace(right))
316     return true;
317   if (canBeJoined(beforeWSpace, right, LangOpts))
318     return false; // the whitespace was intentional, keep it.
319   return true;
320 }
321
322 /// \brief Check the range that we are going to remove and:
323 /// -Remove any trailing whitespace if possible.
324 /// -Insert a space if removing the range is going to mess up the source tokens.
325 static void adjustRemoval(const SourceManager &SM, const LangOptions &LangOpts,
326                           SourceLocation Loc, FileOffset offs,
327                           unsigned &len, StringRef &text) {
328   assert(len && text.empty());
329   SourceLocation BeginTokLoc = Lexer::GetBeginningOfToken(Loc, SM, LangOpts);
330   if (BeginTokLoc != Loc)
331     return; // the range is not at the beginning of a token, keep the range.
332
333   bool Invalid = false;
334   StringRef buffer = SM.getBufferData(offs.getFID(), &Invalid);
335   if (Invalid)
336     return;
337
338   unsigned begin = offs.getOffset();
339   unsigned end = begin + len;
340
341   // Do not try to extend the removal if we're at the end of the buffer already.
342   if (end == buffer.size())
343     return;
344
345   assert(begin < buffer.size() && end < buffer.size() && "Invalid range!");
346
347   // FIXME: Remove newline.
348
349   if (begin == 0) {
350     if (buffer[end] == ' ')
351       ++len;
352     return;
353   }
354
355   if (buffer[end] == ' ') {
356     assert((end + 1 != buffer.size() || buffer.data()[end + 1] == 0) &&
357            "buffer not zero-terminated!");
358     if (canRemoveWhitespace(/*left=*/buffer[begin-1],
359                             /*beforeWSpace=*/buffer[end-1],
360                             /*right=*/buffer.data()[end + 1], // zero-terminated
361                             LangOpts))
362       ++len;
363     return;
364   }
365
366   if (!canBeJoined(buffer[begin-1], buffer[end], LangOpts))
367     text = " ";
368 }
369
370 static void applyRewrite(EditsReceiver &receiver,
371                          StringRef text, FileOffset offs, unsigned len,
372                          const SourceManager &SM, const LangOptions &LangOpts,
373                          bool shouldAdjustRemovals) {
374   assert(offs.getFID().isValid());
375   SourceLocation Loc = SM.getLocForStartOfFile(offs.getFID());
376   Loc = Loc.getLocWithOffset(offs.getOffset());
377   assert(Loc.isFileID());
378
379   if (text.empty() && shouldAdjustRemovals)
380     adjustRemoval(SM, LangOpts, Loc, offs, len, text);
381
382   CharSourceRange range = CharSourceRange::getCharRange(Loc,
383                                                      Loc.getLocWithOffset(len));
384
385   if (text.empty()) {
386     assert(len);
387     receiver.remove(range);
388     return;
389   }
390
391   if (len)
392     receiver.replace(range, text);
393   else
394     receiver.insert(Loc, text);
395 }
396
397 void EditedSource::applyRewrites(EditsReceiver &receiver,
398                                  bool shouldAdjustRemovals) {
399   SmallString<128> StrVec;
400   FileOffset CurOffs, CurEnd;
401   unsigned CurLen;
402
403   if (FileEdits.empty())
404     return;
405
406   FileEditsTy::iterator I = FileEdits.begin();
407   CurOffs = I->first;
408   StrVec = I->second.Text;
409   CurLen = I->second.RemoveLen;
410   CurEnd = CurOffs.getWithOffset(CurLen);
411   ++I;
412
413   for (FileEditsTy::iterator E = FileEdits.end(); I != E; ++I) {
414     FileOffset offs = I->first;
415     FileEdit act = I->second;
416     assert(offs >= CurEnd);
417
418     if (offs == CurEnd) {
419       StrVec += act.Text;
420       CurLen += act.RemoveLen;
421       CurEnd.getWithOffset(act.RemoveLen);
422       continue;
423     }
424
425     applyRewrite(receiver, StrVec, CurOffs, CurLen, SourceMgr, LangOpts,
426                  shouldAdjustRemovals);
427     CurOffs = offs;
428     StrVec = act.Text;
429     CurLen = act.RemoveLen;
430     CurEnd = CurOffs.getWithOffset(CurLen);
431   }
432
433   applyRewrite(receiver, StrVec, CurOffs, CurLen, SourceMgr, LangOpts,
434                shouldAdjustRemovals);
435 }
436
437 void EditedSource::clearRewrites() {
438   FileEdits.clear();
439   StrAlloc.Reset();
440 }
441
442 StringRef EditedSource::getSourceText(FileOffset BeginOffs, FileOffset EndOffs,
443                                       bool &Invalid) {
444   assert(BeginOffs.getFID() == EndOffs.getFID());
445   assert(BeginOffs <= EndOffs);
446   SourceLocation BLoc = SourceMgr.getLocForStartOfFile(BeginOffs.getFID());
447   BLoc = BLoc.getLocWithOffset(BeginOffs.getOffset());
448   assert(BLoc.isFileID());
449   SourceLocation
450     ELoc = BLoc.getLocWithOffset(EndOffs.getOffset() - BeginOffs.getOffset());
451   return Lexer::getSourceText(CharSourceRange::getCharRange(BLoc, ELoc),
452                               SourceMgr, LangOpts, &Invalid);
453 }
454
455 EditedSource::FileEditsTy::iterator
456 EditedSource::getActionForOffset(FileOffset Offs) {
457   FileEditsTy::iterator I = FileEdits.upper_bound(Offs);
458   if (I == FileEdits.begin())
459     return FileEdits.end();
460   --I;
461   FileEdit &FA = I->second;
462   FileOffset B = I->first;
463   FileOffset E = B.getWithOffset(FA.RemoveLen);
464   if (Offs >= B && Offs < E)
465     return I;
466
467   return FileEdits.end();
468 }