EditedSource.cpp 14 KB

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