1239313Sdim//===--- Refactoring.cpp - Framework for clang refactoring tools ----------===//
2239313Sdim//
3239313Sdim//                     The LLVM Compiler Infrastructure
4239313Sdim//
5239313Sdim// This file is distributed under the University of Illinois Open Source
6239313Sdim// License. See LICENSE.TXT for details.
7239313Sdim//
8239313Sdim//===----------------------------------------------------------------------===//
9239313Sdim//
10239313Sdim//  Implements tools to support refactorings.
11239313Sdim//
12239313Sdim//===----------------------------------------------------------------------===//
13239313Sdim
14243830Sdim#include "clang/Basic/DiagnosticOptions.h"
15239313Sdim#include "clang/Basic/FileManager.h"
16239313Sdim#include "clang/Basic/SourceManager.h"
17239313Sdim#include "clang/Frontend/TextDiagnosticPrinter.h"
18239313Sdim#include "clang/Lex/Lexer.h"
19243830Sdim#include "clang/Rewrite/Core/Rewriter.h"
20239313Sdim#include "clang/Tooling/Refactoring.h"
21239313Sdim#include "llvm/Support/raw_os_ostream.h"
22263508Sdim#include "llvm/Support/FileSystem.h"
23263508Sdim#include "llvm/Support/Path.h"
24239313Sdim
25239313Sdimnamespace clang {
26239313Sdimnamespace tooling {
27239313Sdim
28239313Sdimstatic const char * const InvalidLocation = "";
29239313Sdim
30239313SdimReplacement::Replacement()
31263508Sdim  : FilePath(InvalidLocation) {}
32239313Sdim
33263508SdimReplacement::Replacement(StringRef FilePath, unsigned Offset, unsigned Length,
34263508Sdim                         StringRef ReplacementText)
35263508Sdim    : FilePath(FilePath), ReplacementRange(Offset, Length),
36263508Sdim      ReplacementText(ReplacementText) {}
37239313Sdim
38239313SdimReplacement::Replacement(SourceManager &Sources, SourceLocation Start,
39249423Sdim                         unsigned Length, StringRef ReplacementText) {
40239313Sdim  setFromSourceLocation(Sources, Start, Length, ReplacementText);
41239313Sdim}
42239313Sdim
43239313SdimReplacement::Replacement(SourceManager &Sources, const CharSourceRange &Range,
44249423Sdim                         StringRef ReplacementText) {
45239313Sdim  setFromSourceRange(Sources, Range, ReplacementText);
46239313Sdim}
47239313Sdim
48239313Sdimbool Replacement::isApplicable() const {
49239313Sdim  return FilePath != InvalidLocation;
50239313Sdim}
51239313Sdim
52239313Sdimbool Replacement::apply(Rewriter &Rewrite) const {
53239313Sdim  SourceManager &SM = Rewrite.getSourceMgr();
54239313Sdim  const FileEntry *Entry = SM.getFileManager().getFile(FilePath);
55239313Sdim  if (Entry == NULL)
56239313Sdim    return false;
57239313Sdim  FileID ID;
58239313Sdim  // FIXME: Use SM.translateFile directly.
59239313Sdim  SourceLocation Location = SM.translateFileLineCol(Entry, 1, 1);
60239313Sdim  ID = Location.isValid() ?
61239313Sdim    SM.getFileID(Location) :
62239313Sdim    SM.createFileID(Entry, SourceLocation(), SrcMgr::C_User);
63239313Sdim  // FIXME: We cannot check whether Offset + Length is in the file, as
64239313Sdim  // the remapping API is not public in the RewriteBuffer.
65239313Sdim  const SourceLocation Start =
66239313Sdim    SM.getLocForStartOfFile(ID).
67263508Sdim    getLocWithOffset(ReplacementRange.getOffset());
68239313Sdim  // ReplaceText returns false on success.
69239313Sdim  // ReplaceText only fails if the source location is not a file location, in
70239313Sdim  // which case we already returned false earlier.
71263508Sdim  bool RewriteSucceeded = !Rewrite.ReplaceText(
72263508Sdim      Start, ReplacementRange.getLength(), ReplacementText);
73239313Sdim  assert(RewriteSucceeded);
74239313Sdim  return RewriteSucceeded;
75239313Sdim}
76239313Sdim
77239313Sdimstd::string Replacement::toString() const {
78239313Sdim  std::string result;
79239313Sdim  llvm::raw_string_ostream stream(result);
80263508Sdim  stream << FilePath << ": " << ReplacementRange.getOffset() << ":+"
81263508Sdim         << ReplacementRange.getLength() << ":\"" << ReplacementText << "\"";
82239313Sdim  return result;
83239313Sdim}
84239313Sdim
85263508Sdimbool operator<(const Replacement &LHS, const Replacement &RHS) {
86263508Sdim  if (LHS.getOffset() != RHS.getOffset())
87263508Sdim    return LHS.getOffset() < RHS.getOffset();
88263508Sdim  if (LHS.getLength() != RHS.getLength())
89263508Sdim    return LHS.getLength() < RHS.getLength();
90263508Sdim  if (LHS.getFilePath() != RHS.getFilePath())
91263508Sdim    return LHS.getFilePath() < RHS.getFilePath();
92263508Sdim  return LHS.getReplacementText() < RHS.getReplacementText();
93239313Sdim}
94239313Sdim
95263508Sdimbool operator==(const Replacement &LHS, const Replacement &RHS) {
96263508Sdim  return LHS.getOffset() == RHS.getOffset() &&
97263508Sdim         LHS.getLength() == RHS.getLength() &&
98263508Sdim         LHS.getFilePath() == RHS.getFilePath() &&
99263508Sdim         LHS.getReplacementText() == RHS.getReplacementText();
100263508Sdim}
101263508Sdim
102239313Sdimvoid Replacement::setFromSourceLocation(SourceManager &Sources,
103239313Sdim                                        SourceLocation Start, unsigned Length,
104249423Sdim                                        StringRef ReplacementText) {
105239313Sdim  const std::pair<FileID, unsigned> DecomposedLocation =
106239313Sdim      Sources.getDecomposedLoc(Start);
107239313Sdim  const FileEntry *Entry = Sources.getFileEntryForID(DecomposedLocation.first);
108263508Sdim  if (Entry != NULL) {
109263508Sdim    // Make FilePath absolute so replacements can be applied correctly when
110263508Sdim    // relative paths for files are used.
111263508Sdim    llvm::SmallString<256> FilePath(Entry->getName());
112263508Sdim    llvm::error_code EC = llvm::sys::fs::make_absolute(FilePath);
113263508Sdim    this->FilePath = EC ? FilePath.c_str() : Entry->getName();
114263508Sdim  } else {
115263508Sdim    this->FilePath = InvalidLocation;
116263508Sdim  }
117263508Sdim  this->ReplacementRange = Range(DecomposedLocation.second, Length);
118239313Sdim  this->ReplacementText = ReplacementText;
119239313Sdim}
120239313Sdim
121239313Sdim// FIXME: This should go into the Lexer, but we need to figure out how
122239313Sdim// to handle ranges for refactoring in general first - there is no obvious
123239313Sdim// good way how to integrate this into the Lexer yet.
124239313Sdimstatic int getRangeSize(SourceManager &Sources, const CharSourceRange &Range) {
125239313Sdim  SourceLocation SpellingBegin = Sources.getSpellingLoc(Range.getBegin());
126239313Sdim  SourceLocation SpellingEnd = Sources.getSpellingLoc(Range.getEnd());
127239313Sdim  std::pair<FileID, unsigned> Start = Sources.getDecomposedLoc(SpellingBegin);
128239313Sdim  std::pair<FileID, unsigned> End = Sources.getDecomposedLoc(SpellingEnd);
129239313Sdim  if (Start.first != End.first) return -1;
130239313Sdim  if (Range.isTokenRange())
131239313Sdim    End.second += Lexer::MeasureTokenLength(SpellingEnd, Sources,
132239313Sdim                                            LangOptions());
133239313Sdim  return End.second - Start.second;
134239313Sdim}
135239313Sdim
136239313Sdimvoid Replacement::setFromSourceRange(SourceManager &Sources,
137239313Sdim                                     const CharSourceRange &Range,
138249423Sdim                                     StringRef ReplacementText) {
139239313Sdim  setFromSourceLocation(Sources, Sources.getSpellingLoc(Range.getBegin()),
140239313Sdim                        getRangeSize(Sources, Range), ReplacementText);
141239313Sdim}
142239313Sdim
143263508Sdimbool applyAllReplacements(const Replacements &Replaces, Rewriter &Rewrite) {
144239313Sdim  bool Result = true;
145239313Sdim  for (Replacements::const_iterator I = Replaces.begin(),
146239313Sdim                                    E = Replaces.end();
147239313Sdim       I != E; ++I) {
148239313Sdim    if (I->isApplicable()) {
149239313Sdim      Result = I->apply(Rewrite) && Result;
150239313Sdim    } else {
151239313Sdim      Result = false;
152239313Sdim    }
153239313Sdim  }
154239313Sdim  return Result;
155239313Sdim}
156239313Sdim
157263508Sdim// FIXME: Remove this function when Replacements is implemented as std::vector
158263508Sdim// instead of std::set.
159263508Sdimbool applyAllReplacements(const std::vector<Replacement> &Replaces,
160263508Sdim                          Rewriter &Rewrite) {
161263508Sdim  bool Result = true;
162263508Sdim  for (std::vector<Replacement>::const_iterator I = Replaces.begin(),
163263508Sdim                                                E = Replaces.end();
164263508Sdim       I != E; ++I) {
165263508Sdim    if (I->isApplicable()) {
166263508Sdim      Result = I->apply(Rewrite) && Result;
167263508Sdim    } else {
168263508Sdim      Result = false;
169263508Sdim    }
170263508Sdim  }
171263508Sdim  return Result;
172263508Sdim}
173263508Sdim
174263508Sdimstd::string applyAllReplacements(StringRef Code, const Replacements &Replaces) {
175263508Sdim  FileManager Files((FileSystemOptions()));
176263508Sdim  DiagnosticsEngine Diagnostics(
177263508Sdim      IntrusiveRefCntPtr<DiagnosticIDs>(new DiagnosticIDs),
178263508Sdim      new DiagnosticOptions);
179263508Sdim  Diagnostics.setClient(new TextDiagnosticPrinter(
180263508Sdim      llvm::outs(), &Diagnostics.getDiagnosticOptions()));
181263508Sdim  SourceManager SourceMgr(Diagnostics, Files);
182263508Sdim  Rewriter Rewrite(SourceMgr, LangOptions());
183263508Sdim  llvm::MemoryBuffer *Buf = llvm::MemoryBuffer::getMemBuffer(Code, "<stdin>");
184263508Sdim  const clang::FileEntry *Entry =
185263508Sdim      Files.getVirtualFile("<stdin>", Buf->getBufferSize(), 0);
186263508Sdim  SourceMgr.overrideFileContents(Entry, Buf);
187263508Sdim  FileID ID =
188263508Sdim      SourceMgr.createFileID(Entry, SourceLocation(), clang::SrcMgr::C_User);
189263508Sdim  for (Replacements::const_iterator I = Replaces.begin(), E = Replaces.end();
190263508Sdim       I != E; ++I) {
191263508Sdim    Replacement Replace("<stdin>", I->getOffset(), I->getLength(),
192263508Sdim                        I->getReplacementText());
193263508Sdim    if (!Replace.apply(Rewrite))
194263508Sdim      return "";
195263508Sdim  }
196263508Sdim  std::string Result;
197263508Sdim  llvm::raw_string_ostream OS(Result);
198263508Sdim  Rewrite.getEditBuffer(ID).write(OS);
199263508Sdim  OS.flush();
200263508Sdim  return Result;
201263508Sdim}
202263508Sdim
203263508Sdimunsigned shiftedCodePosition(const Replacements &Replaces, unsigned Position) {
204263508Sdim  unsigned NewPosition = Position;
205263508Sdim  for (Replacements::iterator I = Replaces.begin(), E = Replaces.end(); I != E;
206263508Sdim       ++I) {
207263508Sdim    if (I->getOffset() >= Position)
208263508Sdim      break;
209263508Sdim    if (I->getOffset() + I->getLength() > Position)
210263508Sdim      NewPosition += I->getOffset() + I->getLength() - Position;
211263508Sdim    NewPosition += I->getReplacementText().size() - I->getLength();
212263508Sdim  }
213263508Sdim  return NewPosition;
214263508Sdim}
215263508Sdim
216263508Sdim// FIXME: Remove this function when Replacements is implemented as std::vector
217263508Sdim// instead of std::set.
218263508Sdimunsigned shiftedCodePosition(const std::vector<Replacement> &Replaces,
219263508Sdim                             unsigned Position) {
220263508Sdim  unsigned NewPosition = Position;
221263508Sdim  for (std::vector<Replacement>::const_iterator I = Replaces.begin(),
222263508Sdim                                                E = Replaces.end();
223263508Sdim       I != E; ++I) {
224263508Sdim    if (I->getOffset() >= Position)
225263508Sdim      break;
226263508Sdim    if (I->getOffset() + I->getLength() > Position)
227263508Sdim      NewPosition += I->getOffset() + I->getLength() - Position;
228263508Sdim    NewPosition += I->getReplacementText().size() - I->getLength();
229263508Sdim  }
230263508Sdim  return NewPosition;
231263508Sdim}
232263508Sdim
233263508Sdimvoid deduplicate(std::vector<Replacement> &Replaces,
234263508Sdim                 std::vector<Range> &Conflicts) {
235263508Sdim  if (Replaces.empty())
236263508Sdim    return;
237263508Sdim
238263508Sdim  // Deduplicate
239263508Sdim  std::sort(Replaces.begin(), Replaces.end());
240263508Sdim  std::vector<Replacement>::iterator End =
241263508Sdim      std::unique(Replaces.begin(), Replaces.end());
242263508Sdim  Replaces.erase(End, Replaces.end());
243263508Sdim
244263508Sdim  // Detect conflicts
245263508Sdim  Range ConflictRange(Replaces.front().getOffset(),
246263508Sdim                      Replaces.front().getLength());
247263508Sdim  unsigned ConflictStart = 0;
248263508Sdim  unsigned ConflictLength = 1;
249263508Sdim  for (unsigned i = 1; i < Replaces.size(); ++i) {
250263508Sdim    Range Current(Replaces[i].getOffset(), Replaces[i].getLength());
251263508Sdim    if (ConflictRange.overlapsWith(Current)) {
252263508Sdim      // Extend conflicted range
253263508Sdim      ConflictRange = Range(ConflictRange.getOffset(),
254263508Sdim                            std::max(ConflictRange.getLength(),
255263508Sdim                                     Current.getOffset() + Current.getLength() -
256263508Sdim                                         ConflictRange.getOffset()));
257263508Sdim      ++ConflictLength;
258263508Sdim    } else {
259263508Sdim      if (ConflictLength > 1)
260263508Sdim        Conflicts.push_back(Range(ConflictStart, ConflictLength));
261263508Sdim      ConflictRange = Current;
262263508Sdim      ConflictStart = i;
263263508Sdim      ConflictLength = 1;
264263508Sdim    }
265263508Sdim  }
266263508Sdim
267263508Sdim  if (ConflictLength > 1)
268263508Sdim    Conflicts.push_back(Range(ConflictStart, ConflictLength));
269263508Sdim}
270263508Sdim
271263508Sdim
272249423SdimRefactoringTool::RefactoringTool(const CompilationDatabase &Compilations,
273249423Sdim                                 ArrayRef<std::string> SourcePaths)
274249423Sdim  : ClangTool(Compilations, SourcePaths) {}
275249423Sdim
276249423SdimReplacements &RefactoringTool::getReplacements() { return Replace; }
277249423Sdim
278249423Sdimint RefactoringTool::runAndSave(FrontendActionFactory *ActionFactory) {
279249423Sdim  if (int Result = run(ActionFactory)) {
280249423Sdim    return Result;
281249423Sdim  }
282249423Sdim
283249423Sdim  LangOptions DefaultLangOptions;
284249423Sdim  IntrusiveRefCntPtr<DiagnosticOptions> DiagOpts = new DiagnosticOptions();
285249423Sdim  TextDiagnosticPrinter DiagnosticPrinter(llvm::errs(), &*DiagOpts);
286249423Sdim  DiagnosticsEngine Diagnostics(
287249423Sdim      IntrusiveRefCntPtr<DiagnosticIDs>(new DiagnosticIDs()),
288249423Sdim      &*DiagOpts, &DiagnosticPrinter, false);
289249423Sdim  SourceManager Sources(Diagnostics, getFiles());
290249423Sdim  Rewriter Rewrite(Sources, DefaultLangOptions);
291249423Sdim
292249423Sdim  if (!applyAllReplacements(Rewrite)) {
293249423Sdim    llvm::errs() << "Skipped some replacements.\n";
294249423Sdim  }
295249423Sdim
296249423Sdim  return saveRewrittenFiles(Rewrite);
297249423Sdim}
298249423Sdim
299249423Sdimbool RefactoringTool::applyAllReplacements(Rewriter &Rewrite) {
300249423Sdim  return tooling::applyAllReplacements(Replace, Rewrite);
301249423Sdim}
302249423Sdim
303249423Sdimint RefactoringTool::saveRewrittenFiles(Rewriter &Rewrite) {
304263508Sdim  return Rewrite.overwriteChangedFiles() ? 1 : 0;
305239313Sdim}
306239313Sdim
307239313Sdim} // end namespace tooling
308239313Sdim} // end namespace clang
309