EditedSource.cpp 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406
  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/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. using namespace clang;
  18. using namespace edit;
  19. void EditsReceiver::remove(CharSourceRange range) {
  20. replace(range, StringRef());
  21. }
  22. StringRef EditedSource::copyString(const Twine &twine) {
  23. SmallString<128> Data;
  24. return copyString(twine.toStringRef(Data));
  25. }
  26. bool EditedSource::canInsertInOffset(SourceLocation OrigLoc, FileOffset Offs) {
  27. FileEditsTy::iterator FA = getActionForOffset(Offs);
  28. if (FA != FileEdits.end()) {
  29. if (FA->first != Offs)
  30. return false; // position has been removed.
  31. }
  32. if (SourceMgr.isMacroArgExpansion(OrigLoc)) {
  33. SourceLocation
  34. DefArgLoc = SourceMgr.getImmediateExpansionRange(OrigLoc).first;
  35. SourceLocation
  36. ExpLoc = SourceMgr.getImmediateExpansionRange(DefArgLoc).first;
  37. llvm::DenseMap<unsigned, SourceLocation>::iterator
  38. I = ExpansionToArgMap.find(ExpLoc.getRawEncoding());
  39. if (I != ExpansionToArgMap.end() && I->second != DefArgLoc)
  40. return false; // Trying to write in a macro argument input that has
  41. // already been written for another argument of the same macro.
  42. }
  43. return true;
  44. }
  45. bool EditedSource::commitInsert(SourceLocation OrigLoc,
  46. FileOffset Offs, StringRef text,
  47. bool beforePreviousInsertions) {
  48. if (!canInsertInOffset(OrigLoc, Offs))
  49. return false;
  50. if (text.empty())
  51. return true;
  52. if (SourceMgr.isMacroArgExpansion(OrigLoc)) {
  53. SourceLocation
  54. DefArgLoc = SourceMgr.getImmediateExpansionRange(OrigLoc).first;
  55. SourceLocation
  56. ExpLoc = SourceMgr.getImmediateExpansionRange(DefArgLoc).first;
  57. ExpansionToArgMap[ExpLoc.getRawEncoding()] = DefArgLoc;
  58. }
  59. FileEdit &FA = FileEdits[Offs];
  60. if (FA.Text.empty()) {
  61. FA.Text = copyString(text);
  62. return true;
  63. }
  64. if (beforePreviousInsertions)
  65. FA.Text = copyString(Twine(text) + FA.Text);
  66. else
  67. FA.Text = copyString(Twine(FA.Text) + text);
  68. return true;
  69. }
  70. bool EditedSource::commitInsertFromRange(SourceLocation OrigLoc,
  71. FileOffset Offs,
  72. FileOffset InsertFromRangeOffs, unsigned Len,
  73. bool beforePreviousInsertions) {
  74. if (Len == 0)
  75. return true;
  76. SmallString<128> StrVec;
  77. FileOffset BeginOffs = InsertFromRangeOffs;
  78. FileOffset EndOffs = BeginOffs.getWithOffset(Len);
  79. FileEditsTy::iterator I = FileEdits.upper_bound(BeginOffs);
  80. if (I != FileEdits.begin())
  81. --I;
  82. for (; I != FileEdits.end(); ++I) {
  83. FileEdit &FA = I->second;
  84. FileOffset B = I->first;
  85. FileOffset E = B.getWithOffset(FA.RemoveLen);
  86. if (BeginOffs == B)
  87. break;
  88. if (BeginOffs < E) {
  89. if (BeginOffs > B) {
  90. BeginOffs = E;
  91. ++I;
  92. }
  93. break;
  94. }
  95. }
  96. for (; I != FileEdits.end() && EndOffs > I->first; ++I) {
  97. FileEdit &FA = I->second;
  98. FileOffset B = I->first;
  99. FileOffset E = B.getWithOffset(FA.RemoveLen);
  100. if (BeginOffs < B) {
  101. bool Invalid = false;
  102. StringRef text = getSourceText(BeginOffs, B, Invalid);
  103. if (Invalid)
  104. return false;
  105. StrVec += text;
  106. }
  107. StrVec += FA.Text;
  108. BeginOffs = E;
  109. }
  110. if (BeginOffs < EndOffs) {
  111. bool Invalid = false;
  112. StringRef text = getSourceText(BeginOffs, EndOffs, Invalid);
  113. if (Invalid)
  114. return false;
  115. StrVec += text;
  116. }
  117. return commitInsert(OrigLoc, Offs, StrVec, beforePreviousInsertions);
  118. }
  119. void EditedSource::commitRemove(SourceLocation OrigLoc,
  120. FileOffset BeginOffs, unsigned Len) {
  121. if (Len == 0)
  122. return;
  123. FileOffset EndOffs = BeginOffs.getWithOffset(Len);
  124. FileEditsTy::iterator I = FileEdits.upper_bound(BeginOffs);
  125. if (I != FileEdits.begin())
  126. --I;
  127. for (; I != FileEdits.end(); ++I) {
  128. FileEdit &FA = I->second;
  129. FileOffset B = I->first;
  130. FileOffset E = B.getWithOffset(FA.RemoveLen);
  131. if (BeginOffs < E)
  132. break;
  133. }
  134. FileOffset TopBegin, TopEnd;
  135. FileEdit *TopFA = nullptr;
  136. if (I == FileEdits.end()) {
  137. FileEditsTy::iterator
  138. NewI = FileEdits.insert(I, std::make_pair(BeginOffs, FileEdit()));
  139. NewI->second.RemoveLen = Len;
  140. return;
  141. }
  142. FileEdit &FA = I->second;
  143. FileOffset B = I->first;
  144. FileOffset E = B.getWithOffset(FA.RemoveLen);
  145. if (BeginOffs < B) {
  146. FileEditsTy::iterator
  147. NewI = FileEdits.insert(I, std::make_pair(BeginOffs, FileEdit()));
  148. TopBegin = BeginOffs;
  149. TopEnd = EndOffs;
  150. TopFA = &NewI->second;
  151. TopFA->RemoveLen = Len;
  152. } else {
  153. TopBegin = B;
  154. TopEnd = E;
  155. TopFA = &I->second;
  156. if (TopEnd >= EndOffs)
  157. return;
  158. unsigned diff = EndOffs.getOffset() - TopEnd.getOffset();
  159. TopEnd = EndOffs;
  160. TopFA->RemoveLen += diff;
  161. if (B == BeginOffs)
  162. TopFA->Text = StringRef();
  163. ++I;
  164. }
  165. while (I != FileEdits.end()) {
  166. FileEdit &FA = I->second;
  167. FileOffset B = I->first;
  168. FileOffset E = B.getWithOffset(FA.RemoveLen);
  169. if (B >= TopEnd)
  170. break;
  171. if (E <= TopEnd) {
  172. FileEdits.erase(I++);
  173. continue;
  174. }
  175. if (B < TopEnd) {
  176. unsigned diff = E.getOffset() - TopEnd.getOffset();
  177. TopEnd = E;
  178. TopFA->RemoveLen += diff;
  179. FileEdits.erase(I);
  180. }
  181. break;
  182. }
  183. }
  184. bool EditedSource::commit(const Commit &commit) {
  185. if (!commit.isCommitable())
  186. return false;
  187. for (edit::Commit::edit_iterator
  188. I = commit.edit_begin(), E = commit.edit_end(); I != E; ++I) {
  189. const edit::Commit::Edit &edit = *I;
  190. switch (edit.Kind) {
  191. case edit::Commit::Act_Insert:
  192. commitInsert(edit.OrigLoc, edit.Offset, edit.Text, edit.BeforePrev);
  193. break;
  194. case edit::Commit::Act_InsertFromRange:
  195. commitInsertFromRange(edit.OrigLoc, edit.Offset,
  196. edit.InsertFromRangeOffs, edit.Length,
  197. edit.BeforePrev);
  198. break;
  199. case edit::Commit::Act_Remove:
  200. commitRemove(edit.OrigLoc, edit.Offset, edit.Length);
  201. break;
  202. }
  203. }
  204. return true;
  205. }
  206. // \brief Returns true if it is ok to make the two given characters adjacent.
  207. static bool canBeJoined(char left, char right, const LangOptions &LangOpts) {
  208. // FIXME: Should use TokenConcatenation to make sure we don't allow stuff like
  209. // making two '<' adjacent.
  210. return !(Lexer::isIdentifierBodyChar(left, LangOpts) &&
  211. Lexer::isIdentifierBodyChar(right, LangOpts));
  212. }
  213. /// \brief Returns true if it is ok to eliminate the trailing whitespace between
  214. /// the given characters.
  215. static bool canRemoveWhitespace(char left, char beforeWSpace, char right,
  216. const LangOptions &LangOpts) {
  217. if (!canBeJoined(left, right, LangOpts))
  218. return false;
  219. if (isWhitespace(left) || isWhitespace(right))
  220. return true;
  221. if (canBeJoined(beforeWSpace, right, LangOpts))
  222. return false; // the whitespace was intentional, keep it.
  223. return true;
  224. }
  225. /// \brief Check the range that we are going to remove and:
  226. /// -Remove any trailing whitespace if possible.
  227. /// -Insert a space if removing the range is going to mess up the source tokens.
  228. static void adjustRemoval(const SourceManager &SM, const LangOptions &LangOpts,
  229. SourceLocation Loc, FileOffset offs,
  230. unsigned &len, StringRef &text) {
  231. assert(len && text.empty());
  232. SourceLocation BeginTokLoc = Lexer::GetBeginningOfToken(Loc, SM, LangOpts);
  233. if (BeginTokLoc != Loc)
  234. return; // the range is not at the beginning of a token, keep the range.
  235. bool Invalid = false;
  236. StringRef buffer = SM.getBufferData(offs.getFID(), &Invalid);
  237. if (Invalid)
  238. return;
  239. unsigned begin = offs.getOffset();
  240. unsigned end = begin + len;
  241. // Do not try to extend the removal if we're at the end of the buffer already.
  242. if (end == buffer.size())
  243. return;
  244. assert(begin < buffer.size() && end < buffer.size() && "Invalid range!");
  245. // FIXME: Remove newline.
  246. if (begin == 0) {
  247. if (buffer[end] == ' ')
  248. ++len;
  249. return;
  250. }
  251. if (buffer[end] == ' ') {
  252. assert((end + 1 != buffer.size() || buffer.data()[end + 1] == 0) &&
  253. "buffer not zero-terminated!");
  254. if (canRemoveWhitespace(/*left=*/buffer[begin-1],
  255. /*beforeWSpace=*/buffer[end-1],
  256. /*right=*/buffer.data()[end + 1], // zero-terminated
  257. LangOpts))
  258. ++len;
  259. return;
  260. }
  261. if (!canBeJoined(buffer[begin-1], buffer[end], LangOpts))
  262. text = " ";
  263. }
  264. static void applyRewrite(EditsReceiver &receiver,
  265. StringRef text, FileOffset offs, unsigned len,
  266. const SourceManager &SM, const LangOptions &LangOpts) {
  267. assert(!offs.getFID().isInvalid());
  268. SourceLocation Loc = SM.getLocForStartOfFile(offs.getFID());
  269. Loc = Loc.getLocWithOffset(offs.getOffset());
  270. assert(Loc.isFileID());
  271. if (text.empty())
  272. adjustRemoval(SM, LangOpts, Loc, offs, len, text);
  273. CharSourceRange range = CharSourceRange::getCharRange(Loc,
  274. Loc.getLocWithOffset(len));
  275. if (text.empty()) {
  276. assert(len);
  277. receiver.remove(range);
  278. return;
  279. }
  280. if (len)
  281. receiver.replace(range, text);
  282. else
  283. receiver.insert(Loc, text);
  284. }
  285. void EditedSource::applyRewrites(EditsReceiver &receiver) {
  286. SmallString<128> StrVec;
  287. FileOffset CurOffs, CurEnd;
  288. unsigned CurLen;
  289. if (FileEdits.empty())
  290. return;
  291. FileEditsTy::iterator I = FileEdits.begin();
  292. CurOffs = I->first;
  293. StrVec = I->second.Text;
  294. CurLen = I->second.RemoveLen;
  295. CurEnd = CurOffs.getWithOffset(CurLen);
  296. ++I;
  297. for (FileEditsTy::iterator E = FileEdits.end(); I != E; ++I) {
  298. FileOffset offs = I->first;
  299. FileEdit act = I->second;
  300. assert(offs >= CurEnd);
  301. if (offs == CurEnd) {
  302. StrVec += act.Text;
  303. CurLen += act.RemoveLen;
  304. CurEnd.getWithOffset(act.RemoveLen);
  305. continue;
  306. }
  307. applyRewrite(receiver, StrVec, CurOffs, CurLen, SourceMgr, LangOpts);
  308. CurOffs = offs;
  309. StrVec = act.Text;
  310. CurLen = act.RemoveLen;
  311. CurEnd = CurOffs.getWithOffset(CurLen);
  312. }
  313. applyRewrite(receiver, StrVec, CurOffs, CurLen, SourceMgr, LangOpts);
  314. }
  315. void EditedSource::clearRewrites() {
  316. FileEdits.clear();
  317. StrAlloc.Reset();
  318. }
  319. StringRef EditedSource::getSourceText(FileOffset BeginOffs, FileOffset EndOffs,
  320. bool &Invalid) {
  321. assert(BeginOffs.getFID() == EndOffs.getFID());
  322. assert(BeginOffs <= EndOffs);
  323. SourceLocation BLoc = SourceMgr.getLocForStartOfFile(BeginOffs.getFID());
  324. BLoc = BLoc.getLocWithOffset(BeginOffs.getOffset());
  325. assert(BLoc.isFileID());
  326. SourceLocation
  327. ELoc = BLoc.getLocWithOffset(EndOffs.getOffset() - BeginOffs.getOffset());
  328. return Lexer::getSourceText(CharSourceRange::getCharRange(BLoc, ELoc),
  329. SourceMgr, LangOpts, &Invalid);
  330. }
  331. EditedSource::FileEditsTy::iterator
  332. EditedSource::getActionForOffset(FileOffset Offs) {
  333. FileEditsTy::iterator I = FileEdits.upper_bound(Offs);
  334. if (I == FileEdits.begin())
  335. return FileEdits.end();
  336. --I;
  337. FileEdit &FA = I->second;
  338. FileOffset B = I->first;
  339. FileOffset E = B.getWithOffset(FA.RemoveLen);
  340. if (Offs >= B && Offs < E)
  341. return I;
  342. return FileEdits.end();
  343. }