Rewriter.cpp 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478
  1. //===- Rewriter.cpp - Code rewriting interface ----------------------------===//
  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. //
  9. // This file defines the Rewriter class, which is used for code
  10. // transformations.
  11. //
  12. //===----------------------------------------------------------------------===//
  13. #include "clang/Rewrite/Core/Rewriter.h"
  14. #include "clang/Basic/Diagnostic.h"
  15. #include "clang/Basic/DiagnosticIDs.h"
  16. #include "clang/Basic/FileManager.h"
  17. #include "clang/Basic/SourceLocation.h"
  18. #include "clang/Basic/SourceManager.h"
  19. #include "clang/Lex/Lexer.h"
  20. #include "clang/Rewrite/Core/RewriteBuffer.h"
  21. #include "clang/Rewrite/Core/RewriteRope.h"
  22. #include "llvm/ADT/SmallString.h"
  23. #include "llvm/ADT/SmallVector.h"
  24. #include "llvm/ADT/StringRef.h"
  25. #include "llvm/Support/FileSystem.h"
  26. #include "llvm/Support/raw_ostream.h"
  27. #include <cassert>
  28. #include <iterator>
  29. #include <map>
  30. #include <memory>
  31. #include <system_error>
  32. #include <utility>
  33. using namespace clang;
  34. raw_ostream &RewriteBuffer::write(raw_ostream &os) const {
  35. // Walk RewriteRope chunks efficiently using MoveToNextPiece() instead of the
  36. // character iterator.
  37. for (RopePieceBTreeIterator I = begin(), E = end(); I != E;
  38. I.MoveToNextPiece())
  39. os << I.piece();
  40. return os;
  41. }
  42. /// Return true if this character is non-new-line whitespace:
  43. /// ' ', '\\t', '\\f', '\\v', '\\r'.
  44. static inline bool isWhitespaceExceptNL(unsigned char c) {
  45. switch (c) {
  46. case ' ':
  47. case '\t':
  48. case '\f':
  49. case '\v':
  50. case '\r':
  51. return true;
  52. default:
  53. return false;
  54. }
  55. }
  56. void RewriteBuffer::RemoveText(unsigned OrigOffset, unsigned Size,
  57. bool removeLineIfEmpty) {
  58. // Nothing to remove, exit early.
  59. if (Size == 0) return;
  60. unsigned RealOffset = getMappedOffset(OrigOffset, true);
  61. assert(RealOffset+Size <= Buffer.size() && "Invalid location");
  62. // Remove the dead characters.
  63. Buffer.erase(RealOffset, Size);
  64. // Add a delta so that future changes are offset correctly.
  65. AddReplaceDelta(OrigOffset, -Size);
  66. if (removeLineIfEmpty) {
  67. // Find the line that the remove occurred and if it is completely empty
  68. // remove the line as well.
  69. iterator curLineStart = begin();
  70. unsigned curLineStartOffs = 0;
  71. iterator posI = begin();
  72. for (unsigned i = 0; i != RealOffset; ++i) {
  73. if (*posI == '\n') {
  74. curLineStart = posI;
  75. ++curLineStart;
  76. curLineStartOffs = i + 1;
  77. }
  78. ++posI;
  79. }
  80. unsigned lineSize = 0;
  81. posI = curLineStart;
  82. while (posI != end() && isWhitespaceExceptNL(*posI)) {
  83. ++posI;
  84. ++lineSize;
  85. }
  86. if (posI != end() && *posI == '\n') {
  87. Buffer.erase(curLineStartOffs, lineSize + 1/* + '\n'*/);
  88. // FIXME: Here, the offset of the start of the line is supposed to be
  89. // expressed in terms of the original input not the "real" rewrite
  90. // buffer. How do we compute that reliably? It might be tempting to use
  91. // curLineStartOffs + OrigOffset - RealOffset, but that assumes the
  92. // difference between the original and real offset is the same at the
  93. // removed text and at the start of the line, but that's not true if
  94. // edits were previously made earlier on the line. This bug is also
  95. // documented by a FIXME on the definition of
  96. // clang::Rewriter::RewriteOptions::RemoveLineIfEmpty. A reproducer for
  97. // the implementation below is the test RemoveLineIfEmpty in
  98. // clang/unittests/Rewrite/RewriteBufferTest.cpp.
  99. AddReplaceDelta(curLineStartOffs, -(lineSize + 1/* + '\n'*/));
  100. }
  101. }
  102. }
  103. void RewriteBuffer::InsertText(unsigned OrigOffset, StringRef Str,
  104. bool InsertAfter) {
  105. // Nothing to insert, exit early.
  106. if (Str.empty()) return;
  107. unsigned RealOffset = getMappedOffset(OrigOffset, InsertAfter);
  108. Buffer.insert(RealOffset, Str.begin(), Str.end());
  109. // Add a delta so that future changes are offset correctly.
  110. AddInsertDelta(OrigOffset, Str.size());
  111. }
  112. /// ReplaceText - This method replaces a range of characters in the input
  113. /// buffer with a new string. This is effectively a combined "remove+insert"
  114. /// operation.
  115. void RewriteBuffer::ReplaceText(unsigned OrigOffset, unsigned OrigLength,
  116. StringRef NewStr) {
  117. unsigned RealOffset = getMappedOffset(OrigOffset, true);
  118. Buffer.erase(RealOffset, OrigLength);
  119. Buffer.insert(RealOffset, NewStr.begin(), NewStr.end());
  120. if (OrigLength != NewStr.size())
  121. AddReplaceDelta(OrigOffset, NewStr.size() - OrigLength);
  122. }
  123. //===----------------------------------------------------------------------===//
  124. // Rewriter class
  125. //===----------------------------------------------------------------------===//
  126. /// getRangeSize - Return the size in bytes of the specified range if they
  127. /// are in the same file. If not, this returns -1.
  128. int Rewriter::getRangeSize(const CharSourceRange &Range,
  129. RewriteOptions opts) const {
  130. if (!isRewritable(Range.getBegin()) ||
  131. !isRewritable(Range.getEnd())) return -1;
  132. FileID StartFileID, EndFileID;
  133. unsigned StartOff = getLocationOffsetAndFileID(Range.getBegin(), StartFileID);
  134. unsigned EndOff = getLocationOffsetAndFileID(Range.getEnd(), EndFileID);
  135. if (StartFileID != EndFileID)
  136. return -1;
  137. // If edits have been made to this buffer, the delta between the range may
  138. // have changed.
  139. std::map<FileID, RewriteBuffer>::const_iterator I =
  140. RewriteBuffers.find(StartFileID);
  141. if (I != RewriteBuffers.end()) {
  142. const RewriteBuffer &RB = I->second;
  143. EndOff = RB.getMappedOffset(EndOff, opts.IncludeInsertsAtEndOfRange);
  144. StartOff = RB.getMappedOffset(StartOff, !opts.IncludeInsertsAtBeginOfRange);
  145. }
  146. // Adjust the end offset to the end of the last token, instead of being the
  147. // start of the last token if this is a token range.
  148. if (Range.isTokenRange())
  149. EndOff += Lexer::MeasureTokenLength(Range.getEnd(), *SourceMgr, *LangOpts);
  150. return EndOff-StartOff;
  151. }
  152. int Rewriter::getRangeSize(SourceRange Range, RewriteOptions opts) const {
  153. return getRangeSize(CharSourceRange::getTokenRange(Range), opts);
  154. }
  155. /// getRewrittenText - Return the rewritten form of the text in the specified
  156. /// range. If the start or end of the range was unrewritable or if they are
  157. /// in different buffers, this returns an empty string.
  158. ///
  159. /// Note that this method is not particularly efficient.
  160. std::string Rewriter::getRewrittenText(CharSourceRange Range) const {
  161. if (!isRewritable(Range.getBegin()) ||
  162. !isRewritable(Range.getEnd()))
  163. return {};
  164. FileID StartFileID, EndFileID;
  165. unsigned StartOff, EndOff;
  166. StartOff = getLocationOffsetAndFileID(Range.getBegin(), StartFileID);
  167. EndOff = getLocationOffsetAndFileID(Range.getEnd(), EndFileID);
  168. if (StartFileID != EndFileID)
  169. return {}; // Start and end in different buffers.
  170. // If edits have been made to this buffer, the delta between the range may
  171. // have changed.
  172. std::map<FileID, RewriteBuffer>::const_iterator I =
  173. RewriteBuffers.find(StartFileID);
  174. if (I == RewriteBuffers.end()) {
  175. // If the buffer hasn't been rewritten, just return the text from the input.
  176. const char *Ptr = SourceMgr->getCharacterData(Range.getBegin());
  177. // Adjust the end offset to the end of the last token, instead of being the
  178. // start of the last token.
  179. if (Range.isTokenRange())
  180. EndOff +=
  181. Lexer::MeasureTokenLength(Range.getEnd(), *SourceMgr, *LangOpts);
  182. return std::string(Ptr, Ptr+EndOff-StartOff);
  183. }
  184. const RewriteBuffer &RB = I->second;
  185. EndOff = RB.getMappedOffset(EndOff, true);
  186. StartOff = RB.getMappedOffset(StartOff);
  187. // Adjust the end offset to the end of the last token, instead of being the
  188. // start of the last token.
  189. if (Range.isTokenRange())
  190. EndOff += Lexer::MeasureTokenLength(Range.getEnd(), *SourceMgr, *LangOpts);
  191. // Advance the iterators to the right spot, yay for linear time algorithms.
  192. RewriteBuffer::iterator Start = RB.begin();
  193. std::advance(Start, StartOff);
  194. RewriteBuffer::iterator End = Start;
  195. assert(EndOff >= StartOff && "Invalid iteration distance");
  196. std::advance(End, EndOff-StartOff);
  197. return std::string(Start, End);
  198. }
  199. unsigned Rewriter::getLocationOffsetAndFileID(SourceLocation Loc,
  200. FileID &FID) const {
  201. assert(Loc.isValid() && "Invalid location");
  202. std::pair<FileID, unsigned> V = SourceMgr->getDecomposedLoc(Loc);
  203. FID = V.first;
  204. return V.second;
  205. }
  206. /// getEditBuffer - Get or create a RewriteBuffer for the specified FileID.
  207. RewriteBuffer &Rewriter::getEditBuffer(FileID FID) {
  208. std::map<FileID, RewriteBuffer>::iterator I =
  209. RewriteBuffers.lower_bound(FID);
  210. if (I != RewriteBuffers.end() && I->first == FID)
  211. return I->second;
  212. I = RewriteBuffers.insert(I, std::make_pair(FID, RewriteBuffer()));
  213. StringRef MB = SourceMgr->getBufferData(FID);
  214. I->second.Initialize(MB.begin(), MB.end());
  215. return I->second;
  216. }
  217. /// InsertText - Insert the specified string at the specified location in the
  218. /// original buffer.
  219. bool Rewriter::InsertText(SourceLocation Loc, StringRef Str,
  220. bool InsertAfter, bool indentNewLines) {
  221. if (!isRewritable(Loc)) return true;
  222. FileID FID;
  223. unsigned StartOffs = getLocationOffsetAndFileID(Loc, FID);
  224. SmallString<128> indentedStr;
  225. if (indentNewLines && Str.contains('\n')) {
  226. StringRef MB = SourceMgr->getBufferData(FID);
  227. unsigned lineNo = SourceMgr->getLineNumber(FID, StartOffs) - 1;
  228. const SrcMgr::ContentCache *Content =
  229. &SourceMgr->getSLocEntry(FID).getFile().getContentCache();
  230. unsigned lineOffs = Content->SourceLineCache[lineNo];
  231. // Find the whitespace at the start of the line.
  232. StringRef indentSpace;
  233. {
  234. unsigned i = lineOffs;
  235. while (isWhitespaceExceptNL(MB[i]))
  236. ++i;
  237. indentSpace = MB.substr(lineOffs, i-lineOffs);
  238. }
  239. SmallVector<StringRef, 4> lines;
  240. Str.split(lines, "\n");
  241. for (unsigned i = 0, e = lines.size(); i != e; ++i) {
  242. indentedStr += lines[i];
  243. if (i < e-1) {
  244. indentedStr += '\n';
  245. indentedStr += indentSpace;
  246. }
  247. }
  248. Str = indentedStr.str();
  249. }
  250. getEditBuffer(FID).InsertText(StartOffs, Str, InsertAfter);
  251. return false;
  252. }
  253. bool Rewriter::InsertTextAfterToken(SourceLocation Loc, StringRef Str) {
  254. if (!isRewritable(Loc)) return true;
  255. FileID FID;
  256. unsigned StartOffs = getLocationOffsetAndFileID(Loc, FID);
  257. RewriteOptions rangeOpts;
  258. rangeOpts.IncludeInsertsAtBeginOfRange = false;
  259. StartOffs += getRangeSize(SourceRange(Loc, Loc), rangeOpts);
  260. getEditBuffer(FID).InsertText(StartOffs, Str, /*InsertAfter*/true);
  261. return false;
  262. }
  263. /// RemoveText - Remove the specified text region.
  264. bool Rewriter::RemoveText(SourceLocation Start, unsigned Length,
  265. RewriteOptions opts) {
  266. if (!isRewritable(Start)) return true;
  267. FileID FID;
  268. unsigned StartOffs = getLocationOffsetAndFileID(Start, FID);
  269. getEditBuffer(FID).RemoveText(StartOffs, Length, opts.RemoveLineIfEmpty);
  270. return false;
  271. }
  272. /// ReplaceText - This method replaces a range of characters in the input
  273. /// buffer with a new string. This is effectively a combined "remove/insert"
  274. /// operation.
  275. bool Rewriter::ReplaceText(SourceLocation Start, unsigned OrigLength,
  276. StringRef NewStr) {
  277. if (!isRewritable(Start)) return true;
  278. FileID StartFileID;
  279. unsigned StartOffs = getLocationOffsetAndFileID(Start, StartFileID);
  280. getEditBuffer(StartFileID).ReplaceText(StartOffs, OrigLength, NewStr);
  281. return false;
  282. }
  283. bool Rewriter::ReplaceText(SourceRange range, SourceRange replacementRange) {
  284. if (!isRewritable(range.getBegin())) return true;
  285. if (!isRewritable(range.getEnd())) return true;
  286. if (replacementRange.isInvalid()) return true;
  287. SourceLocation start = range.getBegin();
  288. unsigned origLength = getRangeSize(range);
  289. unsigned newLength = getRangeSize(replacementRange);
  290. FileID FID;
  291. unsigned newOffs = getLocationOffsetAndFileID(replacementRange.getBegin(),
  292. FID);
  293. StringRef MB = SourceMgr->getBufferData(FID);
  294. return ReplaceText(start, origLength, MB.substr(newOffs, newLength));
  295. }
  296. bool Rewriter::IncreaseIndentation(CharSourceRange range,
  297. SourceLocation parentIndent) {
  298. if (range.isInvalid()) return true;
  299. if (!isRewritable(range.getBegin())) return true;
  300. if (!isRewritable(range.getEnd())) return true;
  301. if (!isRewritable(parentIndent)) return true;
  302. FileID StartFileID, EndFileID, parentFileID;
  303. unsigned StartOff, EndOff, parentOff;
  304. StartOff = getLocationOffsetAndFileID(range.getBegin(), StartFileID);
  305. EndOff = getLocationOffsetAndFileID(range.getEnd(), EndFileID);
  306. parentOff = getLocationOffsetAndFileID(parentIndent, parentFileID);
  307. if (StartFileID != EndFileID || StartFileID != parentFileID)
  308. return true;
  309. if (StartOff > EndOff)
  310. return true;
  311. FileID FID = StartFileID;
  312. StringRef MB = SourceMgr->getBufferData(FID);
  313. unsigned parentLineNo = SourceMgr->getLineNumber(FID, parentOff) - 1;
  314. unsigned startLineNo = SourceMgr->getLineNumber(FID, StartOff) - 1;
  315. unsigned endLineNo = SourceMgr->getLineNumber(FID, EndOff) - 1;
  316. const SrcMgr::ContentCache *Content =
  317. &SourceMgr->getSLocEntry(FID).getFile().getContentCache();
  318. // Find where the lines start.
  319. unsigned parentLineOffs = Content->SourceLineCache[parentLineNo];
  320. unsigned startLineOffs = Content->SourceLineCache[startLineNo];
  321. // Find the whitespace at the start of each line.
  322. StringRef parentSpace, startSpace;
  323. {
  324. unsigned i = parentLineOffs;
  325. while (isWhitespaceExceptNL(MB[i]))
  326. ++i;
  327. parentSpace = MB.substr(parentLineOffs, i-parentLineOffs);
  328. i = startLineOffs;
  329. while (isWhitespaceExceptNL(MB[i]))
  330. ++i;
  331. startSpace = MB.substr(startLineOffs, i-startLineOffs);
  332. }
  333. if (parentSpace.size() >= startSpace.size())
  334. return true;
  335. if (!startSpace.startswith(parentSpace))
  336. return true;
  337. StringRef indent = startSpace.substr(parentSpace.size());
  338. // Indent the lines between start/end offsets.
  339. RewriteBuffer &RB = getEditBuffer(FID);
  340. for (unsigned lineNo = startLineNo; lineNo <= endLineNo; ++lineNo) {
  341. unsigned offs = Content->SourceLineCache[lineNo];
  342. unsigned i = offs;
  343. while (isWhitespaceExceptNL(MB[i]))
  344. ++i;
  345. StringRef origIndent = MB.substr(offs, i-offs);
  346. if (origIndent.startswith(startSpace))
  347. RB.InsertText(offs, indent, /*InsertAfter=*/false);
  348. }
  349. return false;
  350. }
  351. namespace {
  352. // A wrapper for a file stream that atomically overwrites the target.
  353. //
  354. // Creates a file output stream for a temporary file in the constructor,
  355. // which is later accessible via getStream() if ok() return true.
  356. // Flushes the stream and moves the temporary file to the target location
  357. // in the destructor.
  358. class AtomicallyMovedFile {
  359. public:
  360. AtomicallyMovedFile(DiagnosticsEngine &Diagnostics, StringRef Filename,
  361. bool &AllWritten)
  362. : Diagnostics(Diagnostics), Filename(Filename), AllWritten(AllWritten) {
  363. TempFilename = Filename;
  364. TempFilename += "-%%%%%%%%";
  365. int FD;
  366. if (llvm::sys::fs::createUniqueFile(TempFilename, FD, TempFilename)) {
  367. AllWritten = false;
  368. Diagnostics.Report(clang::diag::err_unable_to_make_temp)
  369. << TempFilename;
  370. } else {
  371. FileStream.reset(new llvm::raw_fd_ostream(FD, /*shouldClose=*/true));
  372. }
  373. }
  374. ~AtomicallyMovedFile() {
  375. if (!ok()) return;
  376. // Close (will also flush) theFileStream.
  377. FileStream->close();
  378. if (std::error_code ec = llvm::sys::fs::rename(TempFilename, Filename)) {
  379. AllWritten = false;
  380. Diagnostics.Report(clang::diag::err_unable_to_rename_temp)
  381. << TempFilename << Filename << ec.message();
  382. // If the remove fails, there's not a lot we can do - this is already an
  383. // error.
  384. llvm::sys::fs::remove(TempFilename);
  385. }
  386. }
  387. bool ok() { return (bool)FileStream; }
  388. raw_ostream &getStream() { return *FileStream; }
  389. private:
  390. DiagnosticsEngine &Diagnostics;
  391. StringRef Filename;
  392. SmallString<128> TempFilename;
  393. std::unique_ptr<llvm::raw_fd_ostream> FileStream;
  394. bool &AllWritten;
  395. };
  396. } // namespace
  397. bool Rewriter::overwriteChangedFiles() {
  398. bool AllWritten = true;
  399. for (buffer_iterator I = buffer_begin(), E = buffer_end(); I != E; ++I) {
  400. const FileEntry *Entry =
  401. getSourceMgr().getFileEntryForID(I->first);
  402. AtomicallyMovedFile File(getSourceMgr().getDiagnostics(), Entry->getName(),
  403. AllWritten);
  404. if (File.ok()) {
  405. I->second.write(File.getStream());
  406. }
  407. }
  408. return !AllWritten;
  409. }