Commit.cpp 10.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346
  1. //===----- Commit.cpp - A unit of 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/Commit.h"
  10. #include "clang/Basic/SourceManager.h"
  11. #include "clang/Edit/EditedSource.h"
  12. #include "clang/Lex/Lexer.h"
  13. #include "clang/Lex/PPConditionalDirectiveRecord.h"
  14. using namespace clang;
  15. using namespace edit;
  16. SourceLocation Commit::Edit::getFileLocation(SourceManager &SM) const {
  17. SourceLocation Loc = SM.getLocForStartOfFile(Offset.getFID());
  18. Loc = Loc.getLocWithOffset(Offset.getOffset());
  19. assert(Loc.isFileID());
  20. return Loc;
  21. }
  22. CharSourceRange Commit::Edit::getFileRange(SourceManager &SM) const {
  23. SourceLocation Loc = getFileLocation(SM);
  24. return CharSourceRange::getCharRange(Loc, Loc.getLocWithOffset(Length));
  25. }
  26. CharSourceRange Commit::Edit::getInsertFromRange(SourceManager &SM) const {
  27. SourceLocation Loc = SM.getLocForStartOfFile(InsertFromRangeOffs.getFID());
  28. Loc = Loc.getLocWithOffset(InsertFromRangeOffs.getOffset());
  29. assert(Loc.isFileID());
  30. return CharSourceRange::getCharRange(Loc, Loc.getLocWithOffset(Length));
  31. }
  32. Commit::Commit(EditedSource &Editor)
  33. : SourceMgr(Editor.getSourceManager()), LangOpts(Editor.getLangOpts()),
  34. PPRec(Editor.getPPCondDirectiveRecord()),
  35. Editor(&Editor), IsCommitable(true) { }
  36. bool Commit::insert(SourceLocation loc, StringRef text,
  37. bool afterToken, bool beforePreviousInsertions) {
  38. if (text.empty())
  39. return true;
  40. FileOffset Offs;
  41. if ((!afterToken && !canInsert(loc, Offs)) ||
  42. ( afterToken && !canInsertAfterToken(loc, Offs, loc))) {
  43. IsCommitable = false;
  44. return false;
  45. }
  46. addInsert(loc, Offs, text, beforePreviousInsertions);
  47. return true;
  48. }
  49. bool Commit::insertFromRange(SourceLocation loc,
  50. CharSourceRange range,
  51. bool afterToken, bool beforePreviousInsertions) {
  52. FileOffset RangeOffs;
  53. unsigned RangeLen;
  54. if (!canRemoveRange(range, RangeOffs, RangeLen)) {
  55. IsCommitable = false;
  56. return false;
  57. }
  58. FileOffset Offs;
  59. if ((!afterToken && !canInsert(loc, Offs)) ||
  60. ( afterToken && !canInsertAfterToken(loc, Offs, loc))) {
  61. IsCommitable = false;
  62. return false;
  63. }
  64. if (PPRec &&
  65. PPRec->areInDifferentConditionalDirectiveRegion(loc, range.getBegin())) {
  66. IsCommitable = false;
  67. return false;
  68. }
  69. addInsertFromRange(loc, Offs, RangeOffs, RangeLen, beforePreviousInsertions);
  70. return true;
  71. }
  72. bool Commit::remove(CharSourceRange range) {
  73. FileOffset Offs;
  74. unsigned Len;
  75. if (!canRemoveRange(range, Offs, Len)) {
  76. IsCommitable = false;
  77. return false;
  78. }
  79. addRemove(range.getBegin(), Offs, Len);
  80. return true;
  81. }
  82. bool Commit::insertWrap(StringRef before, CharSourceRange range,
  83. StringRef after) {
  84. bool commitableBefore = insert(range.getBegin(), before, /*afterToken=*/false,
  85. /*beforePreviousInsertions=*/true);
  86. bool commitableAfter;
  87. if (range.isTokenRange())
  88. commitableAfter = insertAfterToken(range.getEnd(), after);
  89. else
  90. commitableAfter = insert(range.getEnd(), after);
  91. return commitableBefore && commitableAfter;
  92. }
  93. bool Commit::replace(CharSourceRange range, StringRef text) {
  94. if (text.empty())
  95. return remove(range);
  96. FileOffset Offs;
  97. unsigned Len;
  98. if (!canInsert(range.getBegin(), Offs) || !canRemoveRange(range, Offs, Len)) {
  99. IsCommitable = false;
  100. return false;
  101. }
  102. addRemove(range.getBegin(), Offs, Len);
  103. addInsert(range.getBegin(), Offs, text, false);
  104. return true;
  105. }
  106. bool Commit::replaceWithInner(CharSourceRange range,
  107. CharSourceRange replacementRange) {
  108. FileOffset OuterBegin;
  109. unsigned OuterLen;
  110. if (!canRemoveRange(range, OuterBegin, OuterLen)) {
  111. IsCommitable = false;
  112. return false;
  113. }
  114. FileOffset InnerBegin;
  115. unsigned InnerLen;
  116. if (!canRemoveRange(replacementRange, InnerBegin, InnerLen)) {
  117. IsCommitable = false;
  118. return false;
  119. }
  120. FileOffset OuterEnd = OuterBegin.getWithOffset(OuterLen);
  121. FileOffset InnerEnd = InnerBegin.getWithOffset(InnerLen);
  122. if (OuterBegin.getFID() != InnerBegin.getFID() ||
  123. InnerBegin < OuterBegin ||
  124. InnerBegin > OuterEnd ||
  125. InnerEnd > OuterEnd) {
  126. IsCommitable = false;
  127. return false;
  128. }
  129. addRemove(range.getBegin(),
  130. OuterBegin, InnerBegin.getOffset() - OuterBegin.getOffset());
  131. addRemove(replacementRange.getEnd(),
  132. InnerEnd, OuterEnd.getOffset() - InnerEnd.getOffset());
  133. return true;
  134. }
  135. bool Commit::replaceText(SourceLocation loc, StringRef text,
  136. StringRef replacementText) {
  137. if (text.empty() || replacementText.empty())
  138. return true;
  139. FileOffset Offs;
  140. unsigned Len;
  141. if (!canReplaceText(loc, replacementText, Offs, Len)) {
  142. IsCommitable = false;
  143. return false;
  144. }
  145. addRemove(loc, Offs, Len);
  146. addInsert(loc, Offs, text, false);
  147. return true;
  148. }
  149. void Commit::addInsert(SourceLocation OrigLoc, FileOffset Offs, StringRef text,
  150. bool beforePreviousInsertions) {
  151. if (text.empty())
  152. return;
  153. Edit data;
  154. data.Kind = Act_Insert;
  155. data.OrigLoc = OrigLoc;
  156. data.Offset = Offs;
  157. data.Text = copyString(text);
  158. data.BeforePrev = beforePreviousInsertions;
  159. CachedEdits.push_back(data);
  160. }
  161. void Commit::addInsertFromRange(SourceLocation OrigLoc, FileOffset Offs,
  162. FileOffset RangeOffs, unsigned RangeLen,
  163. bool beforePreviousInsertions) {
  164. if (RangeLen == 0)
  165. return;
  166. Edit data;
  167. data.Kind = Act_InsertFromRange;
  168. data.OrigLoc = OrigLoc;
  169. data.Offset = Offs;
  170. data.InsertFromRangeOffs = RangeOffs;
  171. data.Length = RangeLen;
  172. data.BeforePrev = beforePreviousInsertions;
  173. CachedEdits.push_back(data);
  174. }
  175. void Commit::addRemove(SourceLocation OrigLoc,
  176. FileOffset Offs, unsigned Len) {
  177. if (Len == 0)
  178. return;
  179. Edit data;
  180. data.Kind = Act_Remove;
  181. data.OrigLoc = OrigLoc;
  182. data.Offset = Offs;
  183. data.Length = Len;
  184. CachedEdits.push_back(data);
  185. }
  186. bool Commit::canInsert(SourceLocation loc, FileOffset &offs) {
  187. if (loc.isInvalid())
  188. return false;
  189. if (loc.isMacroID())
  190. isAtStartOfMacroExpansion(loc, &loc);
  191. const SourceManager &SM = SourceMgr;
  192. while (SM.isMacroArgExpansion(loc))
  193. loc = SM.getImmediateSpellingLoc(loc);
  194. if (loc.isMacroID())
  195. if (!isAtStartOfMacroExpansion(loc, &loc))
  196. return false;
  197. if (SM.isInSystemHeader(loc))
  198. return false;
  199. std::pair<FileID, unsigned> locInfo = SM.getDecomposedLoc(loc);
  200. if (locInfo.first.isInvalid())
  201. return false;
  202. offs = FileOffset(locInfo.first, locInfo.second);
  203. return canInsertInOffset(loc, offs);
  204. }
  205. bool Commit::canInsertAfterToken(SourceLocation loc, FileOffset &offs,
  206. SourceLocation &AfterLoc) {
  207. if (loc.isInvalid())
  208. return false;
  209. SourceLocation spellLoc = SourceMgr.getSpellingLoc(loc);
  210. unsigned tokLen = Lexer::MeasureTokenLength(spellLoc, SourceMgr, LangOpts);
  211. AfterLoc = loc.getLocWithOffset(tokLen);
  212. if (loc.isMacroID())
  213. isAtEndOfMacroExpansion(loc, &loc);
  214. const SourceManager &SM = SourceMgr;
  215. while (SM.isMacroArgExpansion(loc))
  216. loc = SM.getImmediateSpellingLoc(loc);
  217. if (loc.isMacroID())
  218. if (!isAtEndOfMacroExpansion(loc, &loc))
  219. return false;
  220. if (SM.isInSystemHeader(loc))
  221. return false;
  222. loc = Lexer::getLocForEndOfToken(loc, 0, SourceMgr, LangOpts);
  223. if (loc.isInvalid())
  224. return false;
  225. std::pair<FileID, unsigned> locInfo = SM.getDecomposedLoc(loc);
  226. if (locInfo.first.isInvalid())
  227. return false;
  228. offs = FileOffset(locInfo.first, locInfo.second);
  229. return canInsertInOffset(loc, offs);
  230. }
  231. bool Commit::canInsertInOffset(SourceLocation OrigLoc, FileOffset Offs) {
  232. for (unsigned i = 0, e = CachedEdits.size(); i != e; ++i) {
  233. Edit &act = CachedEdits[i];
  234. if (act.Kind == Act_Remove) {
  235. if (act.Offset.getFID() == Offs.getFID() &&
  236. Offs > act.Offset && Offs < act.Offset.getWithOffset(act.Length))
  237. return false; // position has been removed.
  238. }
  239. }
  240. if (!Editor)
  241. return true;
  242. return Editor->canInsertInOffset(OrigLoc, Offs);
  243. }
  244. bool Commit::canRemoveRange(CharSourceRange range,
  245. FileOffset &Offs, unsigned &Len) {
  246. const SourceManager &SM = SourceMgr;
  247. range = Lexer::makeFileCharRange(range, SM, LangOpts);
  248. if (range.isInvalid())
  249. return false;
  250. if (range.getBegin().isMacroID() || range.getEnd().isMacroID())
  251. return false;
  252. if (SM.isInSystemHeader(range.getBegin()) ||
  253. SM.isInSystemHeader(range.getEnd()))
  254. return false;
  255. if (PPRec && PPRec->rangeIntersectsConditionalDirective(range.getAsRange()))
  256. return false;
  257. std::pair<FileID, unsigned> beginInfo = SM.getDecomposedLoc(range.getBegin());
  258. std::pair<FileID, unsigned> endInfo = SM.getDecomposedLoc(range.getEnd());
  259. if (beginInfo.first != endInfo.first ||
  260. beginInfo.second > endInfo.second)
  261. return false;
  262. Offs = FileOffset(beginInfo.first, beginInfo.second);
  263. Len = endInfo.second - beginInfo.second;
  264. return true;
  265. }
  266. bool Commit::canReplaceText(SourceLocation loc, StringRef text,
  267. FileOffset &Offs, unsigned &Len) {
  268. assert(!text.empty());
  269. if (!canInsert(loc, Offs))
  270. return false;
  271. // Try to load the file buffer.
  272. bool invalidTemp = false;
  273. StringRef file = SourceMgr.getBufferData(Offs.getFID(), &invalidTemp);
  274. if (invalidTemp)
  275. return false;
  276. Len = text.size();
  277. return file.substr(Offs.getOffset()).startswith(text);
  278. }
  279. bool Commit::isAtStartOfMacroExpansion(SourceLocation loc,
  280. SourceLocation *MacroBegin) const {
  281. return Lexer::isAtStartOfMacroExpansion(loc, SourceMgr, LangOpts, MacroBegin);
  282. }
  283. bool Commit::isAtEndOfMacroExpansion(SourceLocation loc,
  284. SourceLocation *MacroEnd) const {
  285. return Lexer::isAtEndOfMacroExpansion(loc, SourceMgr, LangOpts, MacroEnd);
  286. }