Rewriter.cpp 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477
  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. std::advance(End, EndOff-StartOff);
  196. return std::string(Start, End);
  197. }
  198. unsigned Rewriter::getLocationOffsetAndFileID(SourceLocation Loc,
  199. FileID &FID) const {
  200. assert(Loc.isValid() && "Invalid location");
  201. std::pair<FileID, unsigned> V = SourceMgr->getDecomposedLoc(Loc);
  202. FID = V.first;
  203. return V.second;
  204. }
  205. /// getEditBuffer - Get or create a RewriteBuffer for the specified FileID.
  206. RewriteBuffer &Rewriter::getEditBuffer(FileID FID) {
  207. std::map<FileID, RewriteBuffer>::iterator I =
  208. RewriteBuffers.lower_bound(FID);
  209. if (I != RewriteBuffers.end() && I->first == FID)
  210. return I->second;
  211. I = RewriteBuffers.insert(I, std::make_pair(FID, RewriteBuffer()));
  212. StringRef MB = SourceMgr->getBufferData(FID);
  213. I->second.Initialize(MB.begin(), MB.end());
  214. return I->second;
  215. }
  216. /// InsertText - Insert the specified string at the specified location in the
  217. /// original buffer.
  218. bool Rewriter::InsertText(SourceLocation Loc, StringRef Str,
  219. bool InsertAfter, bool indentNewLines) {
  220. if (!isRewritable(Loc)) return true;
  221. FileID FID;
  222. unsigned StartOffs = getLocationOffsetAndFileID(Loc, FID);
  223. SmallString<128> indentedStr;
  224. if (indentNewLines && Str.find('\n') != StringRef::npos) {
  225. StringRef MB = SourceMgr->getBufferData(FID);
  226. unsigned lineNo = SourceMgr->getLineNumber(FID, StartOffs) - 1;
  227. const SrcMgr::ContentCache *
  228. Content = SourceMgr->getSLocEntry(FID).getFile().getContentCache();
  229. unsigned lineOffs = Content->SourceLineCache[lineNo];
  230. // Find the whitespace at the start of the line.
  231. StringRef indentSpace;
  232. {
  233. unsigned i = lineOffs;
  234. while (isWhitespaceExceptNL(MB[i]))
  235. ++i;
  236. indentSpace = MB.substr(lineOffs, i-lineOffs);
  237. }
  238. SmallVector<StringRef, 4> lines;
  239. Str.split(lines, "\n");
  240. for (unsigned i = 0, e = lines.size(); i != e; ++i) {
  241. indentedStr += lines[i];
  242. if (i < e-1) {
  243. indentedStr += '\n';
  244. indentedStr += indentSpace;
  245. }
  246. }
  247. Str = indentedStr.str();
  248. }
  249. getEditBuffer(FID).InsertText(StartOffs, Str, InsertAfter);
  250. return false;
  251. }
  252. bool Rewriter::InsertTextAfterToken(SourceLocation Loc, StringRef Str) {
  253. if (!isRewritable(Loc)) return true;
  254. FileID FID;
  255. unsigned StartOffs = getLocationOffsetAndFileID(Loc, FID);
  256. RewriteOptions rangeOpts;
  257. rangeOpts.IncludeInsertsAtBeginOfRange = false;
  258. StartOffs += getRangeSize(SourceRange(Loc, Loc), rangeOpts);
  259. getEditBuffer(FID).InsertText(StartOffs, Str, /*InsertAfter*/true);
  260. return false;
  261. }
  262. /// RemoveText - Remove the specified text region.
  263. bool Rewriter::RemoveText(SourceLocation Start, unsigned Length,
  264. RewriteOptions opts) {
  265. if (!isRewritable(Start)) return true;
  266. FileID FID;
  267. unsigned StartOffs = getLocationOffsetAndFileID(Start, FID);
  268. getEditBuffer(FID).RemoveText(StartOffs, Length, opts.RemoveLineIfEmpty);
  269. return false;
  270. }
  271. /// ReplaceText - This method replaces a range of characters in the input
  272. /// buffer with a new string. This is effectively a combined "remove/insert"
  273. /// operation.
  274. bool Rewriter::ReplaceText(SourceLocation Start, unsigned OrigLength,
  275. StringRef NewStr) {
  276. if (!isRewritable(Start)) return true;
  277. FileID StartFileID;
  278. unsigned StartOffs = getLocationOffsetAndFileID(Start, StartFileID);
  279. getEditBuffer(StartFileID).ReplaceText(StartOffs, OrigLength, NewStr);
  280. return false;
  281. }
  282. bool Rewriter::ReplaceText(SourceRange range, SourceRange replacementRange) {
  283. if (!isRewritable(range.getBegin())) return true;
  284. if (!isRewritable(range.getEnd())) return true;
  285. if (replacementRange.isInvalid()) return true;
  286. SourceLocation start = range.getBegin();
  287. unsigned origLength = getRangeSize(range);
  288. unsigned newLength = getRangeSize(replacementRange);
  289. FileID FID;
  290. unsigned newOffs = getLocationOffsetAndFileID(replacementRange.getBegin(),
  291. FID);
  292. StringRef MB = SourceMgr->getBufferData(FID);
  293. return ReplaceText(start, origLength, MB.substr(newOffs, newLength));
  294. }
  295. bool Rewriter::IncreaseIndentation(CharSourceRange range,
  296. SourceLocation parentIndent) {
  297. if (range.isInvalid()) return true;
  298. if (!isRewritable(range.getBegin())) return true;
  299. if (!isRewritable(range.getEnd())) return true;
  300. if (!isRewritable(parentIndent)) return true;
  301. FileID StartFileID, EndFileID, parentFileID;
  302. unsigned StartOff, EndOff, parentOff;
  303. StartOff = getLocationOffsetAndFileID(range.getBegin(), StartFileID);
  304. EndOff = getLocationOffsetAndFileID(range.getEnd(), EndFileID);
  305. parentOff = getLocationOffsetAndFileID(parentIndent, parentFileID);
  306. if (StartFileID != EndFileID || StartFileID != parentFileID)
  307. return true;
  308. if (StartOff > EndOff)
  309. return true;
  310. FileID FID = StartFileID;
  311. StringRef MB = SourceMgr->getBufferData(FID);
  312. unsigned parentLineNo = SourceMgr->getLineNumber(FID, parentOff) - 1;
  313. unsigned startLineNo = SourceMgr->getLineNumber(FID, StartOff) - 1;
  314. unsigned endLineNo = SourceMgr->getLineNumber(FID, EndOff) - 1;
  315. const SrcMgr::ContentCache *
  316. Content = SourceMgr->getSLocEntry(FID).getFile().getContentCache();
  317. // Find where the lines start.
  318. unsigned parentLineOffs = Content->SourceLineCache[parentLineNo];
  319. unsigned startLineOffs = Content->SourceLineCache[startLineNo];
  320. // Find the whitespace at the start of each line.
  321. StringRef parentSpace, startSpace;
  322. {
  323. unsigned i = parentLineOffs;
  324. while (isWhitespaceExceptNL(MB[i]))
  325. ++i;
  326. parentSpace = MB.substr(parentLineOffs, i-parentLineOffs);
  327. i = startLineOffs;
  328. while (isWhitespaceExceptNL(MB[i]))
  329. ++i;
  330. startSpace = MB.substr(startLineOffs, i-startLineOffs);
  331. }
  332. if (parentSpace.size() >= startSpace.size())
  333. return true;
  334. if (!startSpace.startswith(parentSpace))
  335. return true;
  336. StringRef indent = startSpace.substr(parentSpace.size());
  337. // Indent the lines between start/end offsets.
  338. RewriteBuffer &RB = getEditBuffer(FID);
  339. for (unsigned lineNo = startLineNo; lineNo <= endLineNo; ++lineNo) {
  340. unsigned offs = Content->SourceLineCache[lineNo];
  341. unsigned i = offs;
  342. while (isWhitespaceExceptNL(MB[i]))
  343. ++i;
  344. StringRef origIndent = MB.substr(offs, i-offs);
  345. if (origIndent.startswith(startSpace))
  346. RB.InsertText(offs, indent, /*InsertAfter=*/false);
  347. }
  348. return false;
  349. }
  350. namespace {
  351. // A wrapper for a file stream that atomically overwrites the target.
  352. //
  353. // Creates a file output stream for a temporary file in the constructor,
  354. // which is later accessible via getStream() if ok() return true.
  355. // Flushes the stream and moves the temporary file to the target location
  356. // in the destructor.
  357. class AtomicallyMovedFile {
  358. public:
  359. AtomicallyMovedFile(DiagnosticsEngine &Diagnostics, StringRef Filename,
  360. bool &AllWritten)
  361. : Diagnostics(Diagnostics), Filename(Filename), AllWritten(AllWritten) {
  362. TempFilename = Filename;
  363. TempFilename += "-%%%%%%%%";
  364. int FD;
  365. if (llvm::sys::fs::createUniqueFile(TempFilename, FD, TempFilename)) {
  366. AllWritten = false;
  367. Diagnostics.Report(clang::diag::err_unable_to_make_temp)
  368. << TempFilename;
  369. } else {
  370. FileStream.reset(new llvm::raw_fd_ostream(FD, /*shouldClose=*/true));
  371. }
  372. }
  373. ~AtomicallyMovedFile() {
  374. if (!ok()) return;
  375. // Close (will also flush) theFileStream.
  376. FileStream->close();
  377. if (std::error_code ec = llvm::sys::fs::rename(TempFilename, Filename)) {
  378. AllWritten = false;
  379. Diagnostics.Report(clang::diag::err_unable_to_rename_temp)
  380. << TempFilename << Filename << ec.message();
  381. // If the remove fails, there's not a lot we can do - this is already an
  382. // error.
  383. llvm::sys::fs::remove(TempFilename);
  384. }
  385. }
  386. bool ok() { return (bool)FileStream; }
  387. raw_ostream &getStream() { return *FileStream; }
  388. private:
  389. DiagnosticsEngine &Diagnostics;
  390. StringRef Filename;
  391. SmallString<128> TempFilename;
  392. std::unique_ptr<llvm::raw_fd_ostream> FileStream;
  393. bool &AllWritten;
  394. };
  395. } // namespace
  396. bool Rewriter::overwriteChangedFiles() {
  397. bool AllWritten = true;
  398. for (buffer_iterator I = buffer_begin(), E = buffer_end(); I != E; ++I) {
  399. const FileEntry *Entry =
  400. getSourceMgr().getFileEntryForID(I->first);
  401. AtomicallyMovedFile File(getSourceMgr().getDiagnostics(), Entry->getName(),
  402. AllWritten);
  403. if (File.ok()) {
  404. I->second.write(File.getStream());
  405. }
  406. }
  407. return !AllWritten;
  408. }