EditedSource.cpp 14 KB

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