Rewriter.cpp 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397
  1. //===--- Rewriter.cpp - Code rewriting interface --------------------------===//
  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. // This file defines the Rewriter class, which is used for code
  11. // transformations.
  12. //
  13. //===----------------------------------------------------------------------===//
  14. #include "clang/Rewrite/Rewriter.h"
  15. #include "clang/AST/Stmt.h"
  16. #include "clang/AST/Decl.h"
  17. #include "clang/Lex/Lexer.h"
  18. #include "clang/Basic/SourceManager.h"
  19. #include "llvm/Support/raw_ostream.h"
  20. using namespace clang;
  21. llvm::raw_ostream &RewriteBuffer::write(llvm::raw_ostream &os) const {
  22. // FIXME: eliminate the copy by writing out each chunk at a time
  23. os << std::string(begin(), end());
  24. return os;
  25. }
  26. /// \brief Return true if this character is non-new-line whitespace:
  27. /// ' ', '\t', '\f', '\v', '\r'.
  28. static inline bool isWhitespace(unsigned char c) {
  29. switch (c) {
  30. case ' ':
  31. case '\t':
  32. case '\f':
  33. case '\v':
  34. case '\r':
  35. return true;
  36. default:
  37. return false;
  38. }
  39. }
  40. void RewriteBuffer::RemoveText(unsigned OrigOffset, unsigned Size,
  41. bool removeLineIfEmpty) {
  42. // Nothing to remove, exit early.
  43. if (Size == 0) return;
  44. unsigned RealOffset = getMappedOffset(OrigOffset, true);
  45. assert(RealOffset+Size < Buffer.size() && "Invalid location");
  46. // Remove the dead characters.
  47. Buffer.erase(RealOffset, Size);
  48. // Add a delta so that future changes are offset correctly.
  49. AddReplaceDelta(OrigOffset, -Size);
  50. if (removeLineIfEmpty) {
  51. // Find the line that the remove occurred and if it is completely empty
  52. // remove the line as well.
  53. iterator curLineStart = begin();
  54. unsigned curLineStartOffs = 0;
  55. iterator posI = begin();
  56. for (unsigned i = 0; i != RealOffset; ++i) {
  57. if (*posI == '\n') {
  58. curLineStart = posI;
  59. ++curLineStart;
  60. curLineStartOffs = i + 1;
  61. }
  62. ++posI;
  63. }
  64. unsigned lineSize = 0;
  65. posI = curLineStart;
  66. while (posI != end() && isWhitespace(*posI)) {
  67. ++posI;
  68. ++lineSize;
  69. }
  70. if (posI != end() && *posI == '\n') {
  71. Buffer.erase(curLineStartOffs, lineSize + 1/* + '\n'*/);
  72. AddReplaceDelta(curLineStartOffs, -(lineSize + 1/* + '\n'*/));
  73. }
  74. }
  75. }
  76. void RewriteBuffer::InsertText(unsigned OrigOffset, llvm::StringRef Str,
  77. bool InsertAfter) {
  78. // Nothing to insert, exit early.
  79. if (Str.empty()) return;
  80. unsigned RealOffset = getMappedOffset(OrigOffset, InsertAfter);
  81. Buffer.insert(RealOffset, Str.begin(), Str.end());
  82. // Add a delta so that future changes are offset correctly.
  83. AddInsertDelta(OrigOffset, Str.size());
  84. }
  85. /// ReplaceText - This method replaces a range of characters in the input
  86. /// buffer with a new string. This is effectively a combined "remove+insert"
  87. /// operation.
  88. void RewriteBuffer::ReplaceText(unsigned OrigOffset, unsigned OrigLength,
  89. llvm::StringRef NewStr) {
  90. unsigned RealOffset = getMappedOffset(OrigOffset, true);
  91. Buffer.erase(RealOffset, OrigLength);
  92. Buffer.insert(RealOffset, NewStr.begin(), NewStr.end());
  93. if (OrigLength != NewStr.size())
  94. AddReplaceDelta(OrigOffset, NewStr.size() - OrigLength);
  95. }
  96. //===----------------------------------------------------------------------===//
  97. // Rewriter class
  98. //===----------------------------------------------------------------------===//
  99. /// getRangeSize - Return the size in bytes of the specified range if they
  100. /// are in the same file. If not, this returns -1.
  101. int Rewriter::getRangeSize(const CharSourceRange &Range,
  102. RewriteOptions opts) const {
  103. if (!isRewritable(Range.getBegin()) ||
  104. !isRewritable(Range.getEnd())) return -1;
  105. FileID StartFileID, EndFileID;
  106. unsigned StartOff, EndOff;
  107. StartOff = getLocationOffsetAndFileID(Range.getBegin(), StartFileID);
  108. EndOff = getLocationOffsetAndFileID(Range.getEnd(), EndFileID);
  109. if (StartFileID != EndFileID)
  110. return -1;
  111. // If edits have been made to this buffer, the delta between the range may
  112. // have changed.
  113. std::map<FileID, RewriteBuffer>::const_iterator I =
  114. RewriteBuffers.find(StartFileID);
  115. if (I != RewriteBuffers.end()) {
  116. const RewriteBuffer &RB = I->second;
  117. EndOff = RB.getMappedOffset(EndOff, opts.IncludeInsertsAtEndOfRange);
  118. StartOff = RB.getMappedOffset(StartOff, !opts.IncludeInsertsAtBeginOfRange);
  119. }
  120. // Adjust the end offset to the end of the last token, instead of being the
  121. // start of the last token if this is a token range.
  122. if (Range.isTokenRange())
  123. EndOff += Lexer::MeasureTokenLength(Range.getEnd(), *SourceMgr, *LangOpts);
  124. return EndOff-StartOff;
  125. }
  126. int Rewriter::getRangeSize(SourceRange Range, RewriteOptions opts) const {
  127. return getRangeSize(CharSourceRange::getTokenRange(Range), opts);
  128. }
  129. /// getRewrittenText - Return the rewritten form of the text in the specified
  130. /// range. If the start or end of the range was unrewritable or if they are
  131. /// in different buffers, this returns an empty string.
  132. ///
  133. /// Note that this method is not particularly efficient.
  134. ///
  135. std::string Rewriter::getRewrittenText(SourceRange Range) const {
  136. if (!isRewritable(Range.getBegin()) ||
  137. !isRewritable(Range.getEnd()))
  138. return "";
  139. FileID StartFileID, EndFileID;
  140. unsigned StartOff, EndOff;
  141. StartOff = getLocationOffsetAndFileID(Range.getBegin(), StartFileID);
  142. EndOff = getLocationOffsetAndFileID(Range.getEnd(), EndFileID);
  143. if (StartFileID != EndFileID)
  144. return ""; // Start and end in different buffers.
  145. // If edits have been made to this buffer, the delta between the range may
  146. // have changed.
  147. std::map<FileID, RewriteBuffer>::const_iterator I =
  148. RewriteBuffers.find(StartFileID);
  149. if (I == RewriteBuffers.end()) {
  150. // If the buffer hasn't been rewritten, just return the text from the input.
  151. const char *Ptr = SourceMgr->getCharacterData(Range.getBegin());
  152. // Adjust the end offset to the end of the last token, instead of being the
  153. // start of the last token.
  154. EndOff += Lexer::MeasureTokenLength(Range.getEnd(), *SourceMgr, *LangOpts);
  155. return std::string(Ptr, Ptr+EndOff-StartOff);
  156. }
  157. const RewriteBuffer &RB = I->second;
  158. EndOff = RB.getMappedOffset(EndOff, true);
  159. StartOff = RB.getMappedOffset(StartOff);
  160. // Adjust the end offset to the end of the last token, instead of being the
  161. // start of the last token.
  162. EndOff += Lexer::MeasureTokenLength(Range.getEnd(), *SourceMgr, *LangOpts);
  163. // Advance the iterators to the right spot, yay for linear time algorithms.
  164. RewriteBuffer::iterator Start = RB.begin();
  165. std::advance(Start, StartOff);
  166. RewriteBuffer::iterator End = Start;
  167. std::advance(End, EndOff-StartOff);
  168. return std::string(Start, End);
  169. }
  170. unsigned Rewriter::getLocationOffsetAndFileID(SourceLocation Loc,
  171. FileID &FID) const {
  172. assert(Loc.isValid() && "Invalid location");
  173. std::pair<FileID,unsigned> V = SourceMgr->getDecomposedLoc(Loc);
  174. FID = V.first;
  175. return V.second;
  176. }
  177. /// getEditBuffer - Get or create a RewriteBuffer for the specified FileID.
  178. ///
  179. RewriteBuffer &Rewriter::getEditBuffer(FileID FID) {
  180. std::map<FileID, RewriteBuffer>::iterator I =
  181. RewriteBuffers.lower_bound(FID);
  182. if (I != RewriteBuffers.end() && I->first == FID)
  183. return I->second;
  184. I = RewriteBuffers.insert(I, std::make_pair(FID, RewriteBuffer()));
  185. llvm::StringRef MB = SourceMgr->getBufferData(FID);
  186. I->second.Initialize(MB.begin(), MB.end());
  187. return I->second;
  188. }
  189. /// InsertText - Insert the specified string at the specified location in the
  190. /// original buffer.
  191. bool Rewriter::InsertText(SourceLocation Loc, llvm::StringRef Str,
  192. bool InsertAfter) {
  193. if (!isRewritable(Loc)) return true;
  194. FileID FID;
  195. unsigned StartOffs = getLocationOffsetAndFileID(Loc, FID);
  196. getEditBuffer(FID).InsertText(StartOffs, Str, InsertAfter);
  197. return false;
  198. }
  199. bool Rewriter::InsertTextAfterToken(SourceLocation Loc, llvm::StringRef Str) {
  200. if (!isRewritable(Loc)) return true;
  201. FileID FID;
  202. unsigned StartOffs = getLocationOffsetAndFileID(Loc, FID);
  203. RewriteOptions rangeOpts;
  204. rangeOpts.IncludeInsertsAtBeginOfRange = false;
  205. StartOffs += getRangeSize(SourceRange(Loc, Loc), rangeOpts);
  206. getEditBuffer(FID).InsertText(StartOffs, Str, /*InsertAfter*/true);
  207. return false;
  208. }
  209. /// RemoveText - Remove the specified text region.
  210. bool Rewriter::RemoveText(SourceLocation Start, unsigned Length,
  211. RewriteOptions opts) {
  212. if (!isRewritable(Start)) return true;
  213. FileID FID;
  214. unsigned StartOffs = getLocationOffsetAndFileID(Start, FID);
  215. getEditBuffer(FID).RemoveText(StartOffs, Length, opts.RemoveLineIfEmpty);
  216. return false;
  217. }
  218. /// ReplaceText - This method replaces a range of characters in the input
  219. /// buffer with a new string. This is effectively a combined "remove/insert"
  220. /// operation.
  221. bool Rewriter::ReplaceText(SourceLocation Start, unsigned OrigLength,
  222. llvm::StringRef NewStr) {
  223. if (!isRewritable(Start)) return true;
  224. FileID StartFileID;
  225. unsigned StartOffs = getLocationOffsetAndFileID(Start, StartFileID);
  226. getEditBuffer(StartFileID).ReplaceText(StartOffs, OrigLength, NewStr);
  227. return false;
  228. }
  229. bool Rewriter::ReplaceText(SourceRange range, SourceRange replacementRange) {
  230. if (!isRewritable(range.getBegin())) return true;
  231. if (!isRewritable(range.getEnd())) return true;
  232. if (replacementRange.isInvalid()) return true;
  233. SourceLocation start = range.getBegin();
  234. unsigned origLength = getRangeSize(range);
  235. unsigned newLength = getRangeSize(replacementRange);
  236. FileID FID;
  237. unsigned newOffs = getLocationOffsetAndFileID(replacementRange.getBegin(),
  238. FID);
  239. llvm::StringRef MB = SourceMgr->getBufferData(FID);
  240. return ReplaceText(start, origLength, MB.substr(newOffs, newLength));
  241. }
  242. /// ReplaceStmt - This replaces a Stmt/Expr with another, using the pretty
  243. /// printer to generate the replacement code. This returns true if the input
  244. /// could not be rewritten, or false if successful.
  245. bool Rewriter::ReplaceStmt(Stmt *From, Stmt *To) {
  246. // Measaure the old text.
  247. int Size = getRangeSize(From->getSourceRange());
  248. if (Size == -1)
  249. return true;
  250. // Get the new text.
  251. std::string SStr;
  252. llvm::raw_string_ostream S(SStr);
  253. To->printPretty(S, 0, PrintingPolicy(*LangOpts));
  254. const std::string &Str = S.str();
  255. ReplaceText(From->getLocStart(), Size, Str);
  256. return false;
  257. }
  258. std::string Rewriter::ConvertToString(Stmt *From) {
  259. std::string SStr;
  260. llvm::raw_string_ostream S(SStr);
  261. From->printPretty(S, 0, PrintingPolicy(*LangOpts));
  262. return SStr;
  263. }
  264. bool Rewriter::IncreaseIndentation(CharSourceRange range,
  265. SourceLocation parentIndent) {
  266. using llvm::StringRef;
  267. if (!isRewritable(range.getBegin())) return true;
  268. if (!isRewritable(range.getEnd())) return true;
  269. if (!isRewritable(parentIndent)) return true;
  270. FileID StartFileID, EndFileID, parentFileID;
  271. unsigned StartOff, EndOff, parentOff;
  272. StartOff = getLocationOffsetAndFileID(range.getBegin(), StartFileID);
  273. EndOff = getLocationOffsetAndFileID(range.getEnd(), EndFileID);
  274. parentOff = getLocationOffsetAndFileID(parentIndent, parentFileID);
  275. if (StartFileID != EndFileID || StartFileID != parentFileID)
  276. return true;
  277. if (StartOff >= EndOff || parentOff >= StartOff)
  278. return true;
  279. FileID FID = StartFileID;
  280. StringRef MB = SourceMgr->getBufferData(FID);
  281. unsigned parentLineNo = SourceMgr->getLineNumber(FID, parentOff) - 1;
  282. unsigned startLineNo = SourceMgr->getLineNumber(FID, StartOff) - 1;
  283. unsigned endLineNo = SourceMgr->getLineNumber(FID, EndOff) - 1;
  284. const SrcMgr::ContentCache *
  285. Content = SourceMgr->getSLocEntry(FID).getFile().getContentCache();
  286. // Find where the line starts for the three offsets.
  287. unsigned parentLineOffs = Content->SourceLineCache[parentLineNo];
  288. unsigned startLineOffs = Content->SourceLineCache[startLineNo];
  289. unsigned endLineOffs = Content->SourceLineCache[endLineNo];
  290. if (startLineOffs == endLineOffs || startLineOffs == parentLineOffs)
  291. return true;
  292. // Find the whitespace at the start of each line.
  293. StringRef parentSpace, startSpace, endSpace;
  294. {
  295. unsigned i = parentLineOffs;
  296. while (isWhitespace(MB[i]))
  297. ++i;
  298. parentSpace = MB.substr(parentLineOffs, i-parentLineOffs);
  299. i = startLineOffs;
  300. while (isWhitespace(MB[i]))
  301. ++i;
  302. startSpace = MB.substr(startLineOffs, i-startLineOffs);
  303. i = endLineOffs;
  304. while (isWhitespace(MB[i]))
  305. ++i;
  306. endSpace = MB.substr(endLineOffs, i-endLineOffs);
  307. }
  308. if (parentSpace.size() >= startSpace.size())
  309. return true;
  310. if (!startSpace.startswith(parentSpace))
  311. return true;
  312. llvm::StringRef indent = startSpace.substr(parentSpace.size());
  313. // Indent the lines between start/end offsets.
  314. RewriteBuffer &RB = getEditBuffer(FID);
  315. for (unsigned i = startLineOffs; i != endLineOffs; ++i) {
  316. if (MB[i] == '\n') {
  317. unsigned startOfLine = i+1;
  318. if (startOfLine == endLineOffs)
  319. break;
  320. StringRef origIndent;
  321. unsigned ws = startOfLine;
  322. while (isWhitespace(MB[ws]))
  323. ++ws;
  324. origIndent = MB.substr(startOfLine, ws-startOfLine);
  325. if (origIndent.startswith(startSpace))
  326. RB.InsertText(startOfLine, indent, /*InsertAfter=*/false);
  327. }
  328. }
  329. return false;
  330. }