Replacement.cpp 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297
  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/Basic/Diagnostic.h"
  14. #include "clang/Basic/DiagnosticIDs.h"
  15. #include "clang/Basic/DiagnosticOptions.h"
  16. #include "clang/Basic/FileManager.h"
  17. #include "clang/Basic/SourceManager.h"
  18. #include "clang/Lex/Lexer.h"
  19. #include "clang/Rewrite/Core/Rewriter.h"
  20. #include "clang/Tooling/Core/Replacement.h"
  21. #include "llvm/Support/FileSystem.h"
  22. #include "llvm/Support/Path.h"
  23. #include "llvm/Support/raw_os_ostream.h"
  24. namespace clang {
  25. namespace tooling {
  26. static const char * const InvalidLocation = "";
  27. Replacement::Replacement()
  28. : FilePath(InvalidLocation) {}
  29. Replacement::Replacement(StringRef FilePath, unsigned Offset, unsigned Length,
  30. StringRef ReplacementText)
  31. : FilePath(FilePath), ReplacementRange(Offset, Length),
  32. ReplacementText(ReplacementText) {}
  33. Replacement::Replacement(const SourceManager &Sources, SourceLocation Start,
  34. unsigned Length, StringRef ReplacementText) {
  35. setFromSourceLocation(Sources, Start, Length, ReplacementText);
  36. }
  37. Replacement::Replacement(const SourceManager &Sources,
  38. const CharSourceRange &Range,
  39. StringRef ReplacementText,
  40. const LangOptions &LangOpts) {
  41. setFromSourceRange(Sources, Range, ReplacementText, LangOpts);
  42. }
  43. bool Replacement::isApplicable() const {
  44. return FilePath != InvalidLocation;
  45. }
  46. bool Replacement::apply(Rewriter &Rewrite) const {
  47. SourceManager &SM = Rewrite.getSourceMgr();
  48. const FileEntry *Entry = SM.getFileManager().getFile(FilePath);
  49. if (!Entry)
  50. return false;
  51. FileID ID;
  52. // FIXME: Use SM.translateFile directly.
  53. SourceLocation Location = SM.translateFileLineCol(Entry, 1, 1);
  54. ID = Location.isValid() ?
  55. SM.getFileID(Location) :
  56. SM.createFileID(Entry, SourceLocation(), SrcMgr::C_User);
  57. // FIXME: We cannot check whether Offset + Length is in the file, as
  58. // the remapping API is not public in the RewriteBuffer.
  59. const SourceLocation Start =
  60. SM.getLocForStartOfFile(ID).
  61. getLocWithOffset(ReplacementRange.getOffset());
  62. // ReplaceText returns false on success.
  63. // ReplaceText only fails if the source location is not a file location, in
  64. // which case we already returned false earlier.
  65. bool RewriteSucceeded = !Rewrite.ReplaceText(
  66. Start, ReplacementRange.getLength(), ReplacementText);
  67. assert(RewriteSucceeded);
  68. return RewriteSucceeded;
  69. }
  70. std::string Replacement::toString() const {
  71. std::string Result;
  72. llvm::raw_string_ostream Stream(Result);
  73. Stream << FilePath << ": " << ReplacementRange.getOffset() << ":+"
  74. << ReplacementRange.getLength() << ":\"" << ReplacementText << "\"";
  75. return Stream.str();
  76. }
  77. bool operator<(const Replacement &LHS, const Replacement &RHS) {
  78. if (LHS.getOffset() != RHS.getOffset())
  79. return LHS.getOffset() < RHS.getOffset();
  80. // Apply longer replacements first, specifically so that deletions are
  81. // executed before insertions. It is (hopefully) never the intention to
  82. // delete parts of newly inserted code.
  83. if (LHS.getLength() != RHS.getLength())
  84. return LHS.getLength() > RHS.getLength();
  85. if (LHS.getFilePath() != RHS.getFilePath())
  86. return LHS.getFilePath() < RHS.getFilePath();
  87. return LHS.getReplacementText() < RHS.getReplacementText();
  88. }
  89. bool operator==(const Replacement &LHS, const Replacement &RHS) {
  90. return LHS.getOffset() == RHS.getOffset() &&
  91. LHS.getLength() == RHS.getLength() &&
  92. LHS.getFilePath() == RHS.getFilePath() &&
  93. LHS.getReplacementText() == RHS.getReplacementText();
  94. }
  95. void Replacement::setFromSourceLocation(const SourceManager &Sources,
  96. SourceLocation Start, unsigned Length,
  97. StringRef ReplacementText) {
  98. const std::pair<FileID, unsigned> DecomposedLocation =
  99. Sources.getDecomposedLoc(Start);
  100. const FileEntry *Entry = Sources.getFileEntryForID(DecomposedLocation.first);
  101. if (Entry) {
  102. // Make FilePath absolute so replacements can be applied correctly when
  103. // relative paths for files are used.
  104. llvm::SmallString<256> FilePath(Entry->getName());
  105. std::error_code EC = llvm::sys::fs::make_absolute(FilePath);
  106. this->FilePath = EC ? FilePath.c_str() : Entry->getName();
  107. } else {
  108. this->FilePath = InvalidLocation;
  109. }
  110. this->ReplacementRange = Range(DecomposedLocation.second, Length);
  111. this->ReplacementText = ReplacementText;
  112. }
  113. // FIXME: This should go into the Lexer, but we need to figure out how
  114. // to handle ranges for refactoring in general first - there is no obvious
  115. // good way how to integrate this into the Lexer yet.
  116. static int getRangeSize(const SourceManager &Sources,
  117. const CharSourceRange &Range,
  118. const LangOptions &LangOpts) {
  119. SourceLocation SpellingBegin = Sources.getSpellingLoc(Range.getBegin());
  120. SourceLocation SpellingEnd = Sources.getSpellingLoc(Range.getEnd());
  121. std::pair<FileID, unsigned> Start = Sources.getDecomposedLoc(SpellingBegin);
  122. std::pair<FileID, unsigned> End = Sources.getDecomposedLoc(SpellingEnd);
  123. if (Start.first != End.first) return -1;
  124. if (Range.isTokenRange())
  125. End.second += Lexer::MeasureTokenLength(SpellingEnd, Sources, LangOpts);
  126. return End.second - Start.second;
  127. }
  128. void Replacement::setFromSourceRange(const SourceManager &Sources,
  129. const CharSourceRange &Range,
  130. StringRef ReplacementText,
  131. const LangOptions &LangOpts) {
  132. setFromSourceLocation(Sources, Sources.getSpellingLoc(Range.getBegin()),
  133. getRangeSize(Sources, Range, LangOpts),
  134. ReplacementText);
  135. }
  136. unsigned shiftedCodePosition(const Replacements &Replaces, unsigned Position) {
  137. unsigned NewPosition = Position;
  138. for (Replacements::iterator I = Replaces.begin(), E = Replaces.end(); I != E;
  139. ++I) {
  140. if (I->getOffset() >= Position)
  141. break;
  142. if (I->getOffset() + I->getLength() > Position)
  143. NewPosition += I->getOffset() + I->getLength() - Position;
  144. NewPosition += I->getReplacementText().size() - I->getLength();
  145. }
  146. return NewPosition;
  147. }
  148. // FIXME: Remove this function when Replacements is implemented as std::vector
  149. // instead of std::set.
  150. unsigned shiftedCodePosition(const std::vector<Replacement> &Replaces,
  151. unsigned Position) {
  152. unsigned NewPosition = Position;
  153. for (std::vector<Replacement>::const_iterator I = Replaces.begin(),
  154. E = Replaces.end();
  155. I != E; ++I) {
  156. if (I->getOffset() >= Position)
  157. break;
  158. if (I->getOffset() + I->getLength() > Position)
  159. NewPosition += I->getOffset() + I->getLength() - Position;
  160. NewPosition += I->getReplacementText().size() - I->getLength();
  161. }
  162. return NewPosition;
  163. }
  164. void deduplicate(std::vector<Replacement> &Replaces,
  165. std::vector<Range> &Conflicts) {
  166. if (Replaces.empty())
  167. return;
  168. auto LessNoPath = [](const Replacement &LHS, const Replacement &RHS) {
  169. if (LHS.getOffset() != RHS.getOffset())
  170. return LHS.getOffset() < RHS.getOffset();
  171. if (LHS.getLength() != RHS.getLength())
  172. return LHS.getLength() < RHS.getLength();
  173. return LHS.getReplacementText() < RHS.getReplacementText();
  174. };
  175. auto EqualNoPath = [](const Replacement &LHS, const Replacement &RHS) {
  176. return LHS.getOffset() == RHS.getOffset() &&
  177. LHS.getLength() == RHS.getLength() &&
  178. LHS.getReplacementText() == RHS.getReplacementText();
  179. };
  180. // Deduplicate. We don't want to deduplicate based on the path as we assume
  181. // that all replacements refer to the same file (or are symlinks).
  182. std::sort(Replaces.begin(), Replaces.end(), LessNoPath);
  183. Replaces.erase(std::unique(Replaces.begin(), Replaces.end(), EqualNoPath),
  184. Replaces.end());
  185. // Detect conflicts
  186. Range ConflictRange(Replaces.front().getOffset(),
  187. Replaces.front().getLength());
  188. unsigned ConflictStart = 0;
  189. unsigned ConflictLength = 1;
  190. for (unsigned i = 1; i < Replaces.size(); ++i) {
  191. Range Current(Replaces[i].getOffset(), Replaces[i].getLength());
  192. if (ConflictRange.overlapsWith(Current)) {
  193. // Extend conflicted range
  194. ConflictRange = Range(ConflictRange.getOffset(),
  195. std::max(ConflictRange.getLength(),
  196. Current.getOffset() + Current.getLength() -
  197. ConflictRange.getOffset()));
  198. ++ConflictLength;
  199. } else {
  200. if (ConflictLength > 1)
  201. Conflicts.push_back(Range(ConflictStart, ConflictLength));
  202. ConflictRange = Current;
  203. ConflictStart = i;
  204. ConflictLength = 1;
  205. }
  206. }
  207. if (ConflictLength > 1)
  208. Conflicts.push_back(Range(ConflictStart, ConflictLength));
  209. }
  210. bool applyAllReplacements(const Replacements &Replaces, Rewriter &Rewrite) {
  211. bool Result = true;
  212. for (Replacements::const_iterator I = Replaces.begin(),
  213. E = Replaces.end();
  214. I != E; ++I) {
  215. if (I->isApplicable()) {
  216. Result = I->apply(Rewrite) && Result;
  217. } else {
  218. Result = false;
  219. }
  220. }
  221. return Result;
  222. }
  223. // FIXME: Remove this function when Replacements is implemented as std::vector
  224. // instead of std::set.
  225. bool applyAllReplacements(const std::vector<Replacement> &Replaces,
  226. Rewriter &Rewrite) {
  227. bool Result = true;
  228. for (std::vector<Replacement>::const_iterator I = Replaces.begin(),
  229. E = Replaces.end();
  230. I != E; ++I) {
  231. if (I->isApplicable()) {
  232. Result = I->apply(Rewrite) && Result;
  233. } else {
  234. Result = false;
  235. }
  236. }
  237. return Result;
  238. }
  239. std::string applyAllReplacements(StringRef Code, const Replacements &Replaces) {
  240. FileManager Files((FileSystemOptions()));
  241. DiagnosticsEngine Diagnostics(
  242. IntrusiveRefCntPtr<DiagnosticIDs>(new DiagnosticIDs),
  243. new DiagnosticOptions);
  244. SourceManager SourceMgr(Diagnostics, Files);
  245. Rewriter Rewrite(SourceMgr, LangOptions());
  246. std::unique_ptr<llvm::MemoryBuffer> Buf =
  247. llvm::MemoryBuffer::getMemBuffer(Code, "<stdin>");
  248. const clang::FileEntry *Entry =
  249. Files.getVirtualFile("<stdin>", Buf->getBufferSize(), 0);
  250. SourceMgr.overrideFileContents(Entry, std::move(Buf));
  251. FileID ID =
  252. SourceMgr.createFileID(Entry, SourceLocation(), clang::SrcMgr::C_User);
  253. for (Replacements::const_iterator I = Replaces.begin(), E = Replaces.end();
  254. I != E; ++I) {
  255. Replacement Replace("<stdin>", I->getOffset(), I->getLength(),
  256. I->getReplacementText());
  257. if (!Replace.apply(Rewrite))
  258. return "";
  259. }
  260. std::string Result;
  261. llvm::raw_string_ostream OS(Result);
  262. Rewrite.getEditBuffer(ID).write(OS);
  263. OS.flush();
  264. return Result;
  265. }
  266. } // end namespace tooling
  267. } // end namespace clang