Replacement.cpp 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487
  1. //===--- Replacement.cpp - Framework for clang refactoring tools ----------===//
  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. // Implements classes to support/store refactorings.
  11. //
  12. //===----------------------------------------------------------------------===//
  13. #include "clang/Tooling/Core/Replacement.h"
  14. #include "clang/Basic/Diagnostic.h"
  15. #include "clang/Basic/DiagnosticIDs.h"
  16. #include "clang/Basic/DiagnosticOptions.h"
  17. #include "clang/Basic/FileManager.h"
  18. #include "clang/Basic/SourceManager.h"
  19. #include "clang/Lex/Lexer.h"
  20. #include "clang/Rewrite/Core/Rewriter.h"
  21. #include "llvm/Support/FileSystem.h"
  22. #include "llvm/Support/raw_os_ostream.h"
  23. namespace clang {
  24. namespace tooling {
  25. static const char * const InvalidLocation = "";
  26. Replacement::Replacement()
  27. : FilePath(InvalidLocation) {}
  28. Replacement::Replacement(StringRef FilePath, unsigned Offset, unsigned Length,
  29. StringRef ReplacementText)
  30. : FilePath(FilePath), ReplacementRange(Offset, Length),
  31. ReplacementText(ReplacementText) {}
  32. Replacement::Replacement(const SourceManager &Sources, SourceLocation Start,
  33. unsigned Length, StringRef ReplacementText) {
  34. setFromSourceLocation(Sources, Start, Length, ReplacementText);
  35. }
  36. Replacement::Replacement(const SourceManager &Sources,
  37. const CharSourceRange &Range,
  38. StringRef ReplacementText,
  39. const LangOptions &LangOpts) {
  40. setFromSourceRange(Sources, Range, ReplacementText, LangOpts);
  41. }
  42. bool Replacement::isApplicable() const {
  43. return FilePath != InvalidLocation;
  44. }
  45. bool Replacement::apply(Rewriter &Rewrite) const {
  46. SourceManager &SM = Rewrite.getSourceMgr();
  47. const FileEntry *Entry = SM.getFileManager().getFile(FilePath);
  48. if (!Entry)
  49. return false;
  50. FileID ID = SM.getOrCreateFileID(Entry, SrcMgr::C_User);
  51. const SourceLocation Start =
  52. SM.getLocForStartOfFile(ID).
  53. getLocWithOffset(ReplacementRange.getOffset());
  54. // ReplaceText returns false on success.
  55. // ReplaceText only fails if the source location is not a file location, in
  56. // which case we already returned false earlier.
  57. bool RewriteSucceeded = !Rewrite.ReplaceText(
  58. Start, ReplacementRange.getLength(), ReplacementText);
  59. assert(RewriteSucceeded);
  60. return RewriteSucceeded;
  61. }
  62. std::string Replacement::toString() const {
  63. std::string Result;
  64. llvm::raw_string_ostream Stream(Result);
  65. Stream << FilePath << ": " << ReplacementRange.getOffset() << ":+"
  66. << ReplacementRange.getLength() << ":\"" << ReplacementText << "\"";
  67. return Stream.str();
  68. }
  69. bool operator<(const Replacement &LHS, const Replacement &RHS) {
  70. if (LHS.getOffset() != RHS.getOffset())
  71. return LHS.getOffset() < RHS.getOffset();
  72. // Apply longer replacements first, specifically so that deletions are
  73. // executed before insertions. It is (hopefully) never the intention to
  74. // delete parts of newly inserted code.
  75. if (LHS.getLength() != RHS.getLength())
  76. return LHS.getLength() > RHS.getLength();
  77. if (LHS.getFilePath() != RHS.getFilePath())
  78. return LHS.getFilePath() < RHS.getFilePath();
  79. return LHS.getReplacementText() < RHS.getReplacementText();
  80. }
  81. bool operator==(const Replacement &LHS, const Replacement &RHS) {
  82. return LHS.getOffset() == RHS.getOffset() &&
  83. LHS.getLength() == RHS.getLength() &&
  84. LHS.getFilePath() == RHS.getFilePath() &&
  85. LHS.getReplacementText() == RHS.getReplacementText();
  86. }
  87. void Replacement::setFromSourceLocation(const SourceManager &Sources,
  88. SourceLocation Start, unsigned Length,
  89. StringRef ReplacementText) {
  90. const std::pair<FileID, unsigned> DecomposedLocation =
  91. Sources.getDecomposedLoc(Start);
  92. const FileEntry *Entry = Sources.getFileEntryForID(DecomposedLocation.first);
  93. this->FilePath = Entry ? Entry->getName() : InvalidLocation;
  94. this->ReplacementRange = Range(DecomposedLocation.second, Length);
  95. this->ReplacementText = ReplacementText;
  96. }
  97. // FIXME: This should go into the Lexer, but we need to figure out how
  98. // to handle ranges for refactoring in general first - there is no obvious
  99. // good way how to integrate this into the Lexer yet.
  100. static int getRangeSize(const SourceManager &Sources,
  101. const CharSourceRange &Range,
  102. const LangOptions &LangOpts) {
  103. SourceLocation SpellingBegin = Sources.getSpellingLoc(Range.getBegin());
  104. SourceLocation SpellingEnd = Sources.getSpellingLoc(Range.getEnd());
  105. std::pair<FileID, unsigned> Start = Sources.getDecomposedLoc(SpellingBegin);
  106. std::pair<FileID, unsigned> End = Sources.getDecomposedLoc(SpellingEnd);
  107. if (Start.first != End.first) return -1;
  108. if (Range.isTokenRange())
  109. End.second += Lexer::MeasureTokenLength(SpellingEnd, Sources, LangOpts);
  110. return End.second - Start.second;
  111. }
  112. void Replacement::setFromSourceRange(const SourceManager &Sources,
  113. const CharSourceRange &Range,
  114. StringRef ReplacementText,
  115. const LangOptions &LangOpts) {
  116. setFromSourceLocation(Sources, Sources.getSpellingLoc(Range.getBegin()),
  117. getRangeSize(Sources, Range, LangOpts),
  118. ReplacementText);
  119. }
  120. llvm::Error makeConflictReplacementsError(const Replacement &New,
  121. const Replacement &Existing) {
  122. return llvm::make_error<llvm::StringError>(
  123. "New replacement:\n" + New.toString() +
  124. "\nconflicts with existing replacement:\n" + Existing.toString(),
  125. llvm::inconvertibleErrorCode());
  126. }
  127. llvm::Error Replacements::add(const Replacement &R) {
  128. // Check the file path.
  129. if (!Replaces.empty() && R.getFilePath() != Replaces.begin()->getFilePath())
  130. return llvm::make_error<llvm::StringError>(
  131. "All replacements must have the same file path. New replacement: " +
  132. R.getFilePath() + ", existing replacements: " +
  133. Replaces.begin()->getFilePath() + "\n",
  134. llvm::inconvertibleErrorCode());
  135. // Special-case header insertions.
  136. if (R.getOffset() == UINT_MAX) {
  137. Replaces.insert(R);
  138. return llvm::Error::success();
  139. }
  140. // This replacement cannot conflict with replacements that end before
  141. // this replacement starts or start after this replacement ends.
  142. // We also know that there currently are no overlapping replacements.
  143. // Thus, we know that all replacements that start after the end of the current
  144. // replacement cannot overlap.
  145. Replacement AtEnd(R.getFilePath(), R.getOffset() + R.getLength(), 0, "");
  146. // Find the first entry that starts after or at the end of R. Note that
  147. // entries that start at the end can still be conflicting if R is an
  148. // insertion.
  149. auto I = Replaces.lower_bound(AtEnd);
  150. // If `I` starts at the same offset as `R`, `R` must be an insertion.
  151. if (I != Replaces.end() && R.getOffset() == I->getOffset()) {
  152. assert(R.getLength() == 0);
  153. // `I` is also an insertion, `R` and `I` conflict.
  154. if (I->getLength() == 0)
  155. return makeConflictReplacementsError(R, *I);
  156. // Insertion `R` is adjacent to a non-insertion replacement `I`, so they
  157. // are order-independent. It is safe to assume that `R` will not conflict
  158. // with any replacement before `I` since all replacements before `I` must
  159. // either end before `R` or end at `R` but has length > 0 (if the
  160. // replacement before `I` is an insertion at `R`, it would have been `I`
  161. // since it is a lower bound of `AtEnd` and ordered before the current `I`
  162. // in the set).
  163. Replaces.insert(R);
  164. return llvm::Error::success();
  165. }
  166. // I is the smallest iterator whose entry cannot overlap.
  167. // If that is begin(), there are no overlaps.
  168. if (I == Replaces.begin()) {
  169. Replaces.insert(R);
  170. return llvm::Error::success();
  171. }
  172. --I;
  173. // If the previous entry does not overlap, we know that entries before it
  174. // can also not overlap.
  175. if (!Range(R.getOffset(), R.getLength())
  176. .overlapsWith(Range(I->getOffset(), I->getLength()))) {
  177. // If `R` and `I` do not have the same offset, it is safe to add `R` since
  178. // it must come after `I`. Otherwise:
  179. // - If `R` is an insertion, `I` must not be an insertion since it would
  180. // have come after `AtEnd` if it has length 0.
  181. // - If `R` is not an insertion, `I` must be an insertion; otherwise, `R`
  182. // and `I` would have overlapped.
  183. // In either case, we can safely insert `R`.
  184. Replaces.insert(R);
  185. return llvm::Error::success();
  186. }
  187. return makeConflictReplacementsError(R, *I);
  188. }
  189. namespace {
  190. // Represents a merged replacement, i.e. a replacement consisting of multiple
  191. // overlapping replacements from 'First' and 'Second' in mergeReplacements.
  192. //
  193. // Position projection:
  194. // Offsets and lengths of the replacements can generally refer to two different
  195. // coordinate spaces. Replacements from 'First' refer to the original text
  196. // whereas replacements from 'Second' refer to the text after applying 'First'.
  197. //
  198. // MergedReplacement always operates in the coordinate space of the original
  199. // text, i.e. transforms elements from 'Second' to take into account what was
  200. // changed based on the elements from 'First'.
  201. //
  202. // We can correctly calculate this projection as we look at the replacements in
  203. // order of strictly increasing offsets.
  204. //
  205. // Invariants:
  206. // * We always merge elements from 'First' into elements from 'Second' and vice
  207. // versa. Within each set, the replacements are non-overlapping.
  208. // * We only extend to the right, i.e. merge elements with strictly increasing
  209. // offsets.
  210. class MergedReplacement {
  211. public:
  212. MergedReplacement(const Replacement &R, bool MergeSecond, int D)
  213. : MergeSecond(MergeSecond), Delta(D), FilePath(R.getFilePath()),
  214. Offset(R.getOffset() + (MergeSecond ? 0 : Delta)), Length(R.getLength()),
  215. Text(R.getReplacementText()) {
  216. Delta += MergeSecond ? 0 : Text.size() - Length;
  217. DeltaFirst = MergeSecond ? Text.size() - Length : 0;
  218. }
  219. // Merges the next element 'R' into this merged element. As we always merge
  220. // from 'First' into 'Second' or vice versa, the MergedReplacement knows what
  221. // set the next element is coming from.
  222. void merge(const Replacement &R) {
  223. if (MergeSecond) {
  224. unsigned REnd = R.getOffset() + Delta + R.getLength();
  225. unsigned End = Offset + Text.size();
  226. if (REnd > End) {
  227. Length += REnd - End;
  228. MergeSecond = false;
  229. }
  230. StringRef TextRef = Text;
  231. StringRef Head = TextRef.substr(0, R.getOffset() + Delta - Offset);
  232. StringRef Tail = TextRef.substr(REnd - Offset);
  233. Text = (Head + R.getReplacementText() + Tail).str();
  234. Delta += R.getReplacementText().size() - R.getLength();
  235. } else {
  236. unsigned End = Offset + Length;
  237. StringRef RText = R.getReplacementText();
  238. StringRef Tail = RText.substr(End - R.getOffset());
  239. Text = (Text + Tail).str();
  240. if (R.getOffset() + RText.size() > End) {
  241. Length = R.getOffset() + R.getLength() - Offset;
  242. MergeSecond = true;
  243. } else {
  244. Length += R.getLength() - RText.size();
  245. }
  246. DeltaFirst += RText.size() - R.getLength();
  247. }
  248. }
  249. // Returns 'true' if 'R' starts strictly after the MergedReplacement and thus
  250. // doesn't need to be merged.
  251. bool endsBefore(const Replacement &R) const {
  252. if (MergeSecond)
  253. return Offset + Text.size() < R.getOffset() + Delta;
  254. return Offset + Length < R.getOffset();
  255. }
  256. // Returns 'true' if an element from the second set should be merged next.
  257. bool mergeSecond() const { return MergeSecond; }
  258. int deltaFirst() const { return DeltaFirst; }
  259. Replacement asReplacement() const { return {FilePath, Offset, Length, Text}; }
  260. private:
  261. bool MergeSecond;
  262. // Amount of characters that elements from 'Second' need to be shifted by in
  263. // order to refer to the original text.
  264. int Delta;
  265. // Sum of all deltas (text-length - length) of elements from 'First' merged
  266. // into this element. This is used to update 'Delta' once the
  267. // MergedReplacement is completed.
  268. int DeltaFirst;
  269. // Data of the actually merged replacement. FilePath and Offset aren't changed
  270. // as the element is only extended to the right.
  271. const StringRef FilePath;
  272. const unsigned Offset;
  273. unsigned Length;
  274. std::string Text;
  275. };
  276. } // namespace
  277. Replacements Replacements::merge(const Replacements &ReplacesToMerge) const {
  278. if (empty() || ReplacesToMerge.empty())
  279. return empty() ? ReplacesToMerge : *this;
  280. auto &First = Replaces;
  281. auto &Second = ReplacesToMerge.Replaces;
  282. // Delta is the amount of characters that replacements from 'Second' need to
  283. // be shifted so that their offsets refer to the original text.
  284. int Delta = 0;
  285. ReplacementsImpl Result;
  286. // Iterate over both sets and always add the next element (smallest total
  287. // Offset) from either 'First' or 'Second'. Merge that element with
  288. // subsequent replacements as long as they overlap. See more details in the
  289. // comment on MergedReplacement.
  290. for (auto FirstI = First.begin(), SecondI = Second.begin();
  291. FirstI != First.end() || SecondI != Second.end();) {
  292. bool NextIsFirst = SecondI == Second.end() ||
  293. (FirstI != First.end() &&
  294. FirstI->getOffset() < SecondI->getOffset() + Delta);
  295. MergedReplacement Merged(NextIsFirst ? *FirstI : *SecondI, NextIsFirst,
  296. Delta);
  297. ++(NextIsFirst ? FirstI : SecondI);
  298. while ((Merged.mergeSecond() && SecondI != Second.end()) ||
  299. (!Merged.mergeSecond() && FirstI != First.end())) {
  300. auto &I = Merged.mergeSecond() ? SecondI : FirstI;
  301. if (Merged.endsBefore(*I))
  302. break;
  303. Merged.merge(*I);
  304. ++I;
  305. }
  306. Delta -= Merged.deltaFirst();
  307. Result.insert(Merged.asReplacement());
  308. }
  309. return Replacements(Result.begin(), Result.end());
  310. }
  311. // Combines overlapping ranges in \p Ranges and sorts the combined ranges.
  312. // Returns a set of non-overlapping and sorted ranges that is equivalent to
  313. // \p Ranges.
  314. static std::vector<Range> combineAndSortRanges(std::vector<Range> Ranges) {
  315. std::sort(Ranges.begin(), Ranges.end(),
  316. [](const Range &LHS, const Range &RHS) {
  317. if (LHS.getOffset() != RHS.getOffset())
  318. return LHS.getOffset() < RHS.getOffset();
  319. return LHS.getLength() < RHS.getLength();
  320. });
  321. std::vector<Range> Result;
  322. for (const auto &R : Ranges) {
  323. if (Result.empty() ||
  324. Result.back().getOffset() + Result.back().getLength() < R.getOffset()) {
  325. Result.push_back(R);
  326. } else {
  327. unsigned NewEnd =
  328. std::max(Result.back().getOffset() + Result.back().getLength(),
  329. R.getOffset() + R.getLength());
  330. Result[Result.size() - 1] =
  331. Range(Result.back().getOffset(), NewEnd - Result.back().getOffset());
  332. }
  333. }
  334. return Result;
  335. }
  336. std::vector<Range>
  337. calculateRangesAfterReplacements(const Replacements &Replaces,
  338. const std::vector<Range> &Ranges) {
  339. // To calculate the new ranges,
  340. // - Turn \p Ranges into Replacements at (offset, length) with an empty
  341. // (unimportant) replacement text of length "length".
  342. // - Merge with \p Replaces.
  343. // - The new ranges will be the affected ranges of the merged replacements.
  344. auto MergedRanges = combineAndSortRanges(Ranges);
  345. if (Replaces.empty())
  346. return MergedRanges;
  347. tooling::Replacements FakeReplaces;
  348. for (const auto &R : MergedRanges) {
  349. auto Err = FakeReplaces.add(Replacement(Replaces.begin()->getFilePath(),
  350. R.getOffset(), R.getLength(),
  351. std::string(R.getLength(), ' ')));
  352. assert(!Err &&
  353. "Replacements must not conflict since ranges have been merged.");
  354. (void)Err;
  355. }
  356. return FakeReplaces.merge(Replaces).getAffectedRanges();
  357. }
  358. std::vector<Range> Replacements::getAffectedRanges() const {
  359. std::vector<Range> ChangedRanges;
  360. int Shift = 0;
  361. for (const Replacement &R : Replaces) {
  362. unsigned Offset = R.getOffset() + Shift;
  363. unsigned Length = R.getReplacementText().size();
  364. Shift += Length - R.getLength();
  365. ChangedRanges.push_back(Range(Offset, Length));
  366. }
  367. return combineAndSortRanges(ChangedRanges);
  368. }
  369. unsigned Replacements::getShiftedCodePosition(unsigned Position) const {
  370. unsigned Offset = 0;
  371. for (const auto& R : Replaces) {
  372. if (R.getOffset() + R.getLength() <= Position) {
  373. Offset += R.getReplacementText().size() - R.getLength();
  374. continue;
  375. }
  376. if (R.getOffset() < Position &&
  377. R.getOffset() + R.getReplacementText().size() <= Position) {
  378. Position = R.getOffset() + R.getReplacementText().size();
  379. if (R.getReplacementText().size() > 0)
  380. Position--;
  381. }
  382. break;
  383. }
  384. return Position + Offset;
  385. }
  386. bool applyAllReplacements(const Replacements &Replaces, Rewriter &Rewrite) {
  387. bool Result = true;
  388. for (Replacements::const_iterator I = Replaces.begin(),
  389. E = Replaces.end();
  390. I != E; ++I) {
  391. if (I->isApplicable()) {
  392. Result = I->apply(Rewrite) && Result;
  393. } else {
  394. Result = false;
  395. }
  396. }
  397. return Result;
  398. }
  399. llvm::Expected<std::string> applyAllReplacements(StringRef Code,
  400. const Replacements &Replaces) {
  401. if (Replaces.empty())
  402. return Code.str();
  403. IntrusiveRefCntPtr<vfs::InMemoryFileSystem> InMemoryFileSystem(
  404. new vfs::InMemoryFileSystem);
  405. FileManager Files(FileSystemOptions(), InMemoryFileSystem);
  406. DiagnosticsEngine Diagnostics(
  407. IntrusiveRefCntPtr<DiagnosticIDs>(new DiagnosticIDs),
  408. new DiagnosticOptions);
  409. SourceManager SourceMgr(Diagnostics, Files);
  410. Rewriter Rewrite(SourceMgr, LangOptions());
  411. InMemoryFileSystem->addFile(
  412. "<stdin>", 0, llvm::MemoryBuffer::getMemBuffer(Code, "<stdin>"));
  413. FileID ID = SourceMgr.createFileID(Files.getFile("<stdin>"), SourceLocation(),
  414. clang::SrcMgr::C_User);
  415. for (Replacements::const_iterator I = Replaces.begin(), E = Replaces.end();
  416. I != E; ++I) {
  417. Replacement Replace("<stdin>", I->getOffset(), I->getLength(),
  418. I->getReplacementText());
  419. if (!Replace.apply(Rewrite))
  420. return llvm::make_error<llvm::StringError>(
  421. "Failed to apply replacement: " + Replace.toString(),
  422. llvm::inconvertibleErrorCode());
  423. }
  424. std::string Result;
  425. llvm::raw_string_ostream OS(Result);
  426. Rewrite.getEditBuffer(ID).write(OS);
  427. OS.flush();
  428. return Result;
  429. }
  430. std::map<std::string, Replacements>
  431. groupReplacementsByFile(const Replacements &Replaces) {
  432. std::map<std::string, Replacements> FileToReplaces;
  433. for (const auto &Replace : Replaces)
  434. // We can ignore the Error here since \p Replaces is already conflict-free.
  435. FileToReplaces[Replace.getFilePath()].add(Replace);
  436. return FileToReplaces;
  437. }
  438. } // end namespace tooling
  439. } // end namespace clang