blob: f795def74ff564201eaaee46b07f5ff1cb130c65 [file] [log] [blame]
Manuel Klimekf9d4cbd2012-05-23 16:29:20 +00001//===--- Refactoring.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 tools to support refactorings.
11//
12//===----------------------------------------------------------------------===//
13
Douglas Gregor02c23eb2012-10-23 22:26:28 +000014#include "clang/Basic/DiagnosticOptions.h"
Manuel Klimekf9d4cbd2012-05-23 16:29:20 +000015#include "clang/Basic/FileManager.h"
16#include "clang/Basic/SourceManager.h"
Manuel Klimekf9d4cbd2012-05-23 16:29:20 +000017#include "clang/Frontend/TextDiagnosticPrinter.h"
18#include "clang/Lex/Lexer.h"
Ted Kremenek305c6132012-09-01 05:09:24 +000019#include "clang/Rewrite/Core/Rewriter.h"
Manuel Klimekf9d4cbd2012-05-23 16:29:20 +000020#include "clang/Tooling/Refactoring.h"
21#include "llvm/Support/raw_os_ostream.h"
22
23namespace clang {
24namespace tooling {
25
26static const char * const InvalidLocation = "";
27
28Replacement::Replacement()
Daniel Jasper8a999452013-05-16 10:40:07 +000029 : FilePath(InvalidLocation) {}
Manuel Klimekf9d4cbd2012-05-23 16:29:20 +000030
Daniel Jasper8a999452013-05-16 10:40:07 +000031Replacement::Replacement(StringRef FilePath, unsigned Offset, unsigned Length,
32 StringRef ReplacementText)
33 : FilePath(FilePath), ReplacementRange(Offset, Length),
34 ReplacementText(ReplacementText) {}
Manuel Klimekf9d4cbd2012-05-23 16:29:20 +000035
36Replacement::Replacement(SourceManager &Sources, SourceLocation Start,
Dmitri Gribenkocfa88f82013-01-12 19:30:44 +000037 unsigned Length, StringRef ReplacementText) {
Manuel Klimekf9d4cbd2012-05-23 16:29:20 +000038 setFromSourceLocation(Sources, Start, Length, ReplacementText);
39}
40
41Replacement::Replacement(SourceManager &Sources, const CharSourceRange &Range,
Dmitri Gribenkocfa88f82013-01-12 19:30:44 +000042 StringRef ReplacementText) {
Manuel Klimekf9d4cbd2012-05-23 16:29:20 +000043 setFromSourceRange(Sources, Range, ReplacementText);
44}
45
46bool Replacement::isApplicable() const {
47 return FilePath != InvalidLocation;
48}
49
50bool Replacement::apply(Rewriter &Rewrite) const {
51 SourceManager &SM = Rewrite.getSourceMgr();
52 const FileEntry *Entry = SM.getFileManager().getFile(FilePath);
53 if (Entry == NULL)
54 return false;
55 FileID ID;
56 // FIXME: Use SM.translateFile directly.
57 SourceLocation Location = SM.translateFileLineCol(Entry, 1, 1);
58 ID = Location.isValid() ?
59 SM.getFileID(Location) :
60 SM.createFileID(Entry, SourceLocation(), SrcMgr::C_User);
61 // FIXME: We cannot check whether Offset + Length is in the file, as
62 // the remapping API is not public in the RewriteBuffer.
63 const SourceLocation Start =
64 SM.getLocForStartOfFile(ID).
Daniel Jasper8a999452013-05-16 10:40:07 +000065 getLocWithOffset(ReplacementRange.getOffset());
Manuel Klimekf9d4cbd2012-05-23 16:29:20 +000066 // ReplaceText returns false on success.
67 // ReplaceText only fails if the source location is not a file location, in
68 // which case we already returned false earlier.
Daniel Jasper8a999452013-05-16 10:40:07 +000069 bool RewriteSucceeded = !Rewrite.ReplaceText(
70 Start, ReplacementRange.getLength(), ReplacementText);
Manuel Klimekf9d4cbd2012-05-23 16:29:20 +000071 assert(RewriteSucceeded);
72 return RewriteSucceeded;
73}
74
Manuel Klimek5d51e882012-05-30 16:04:29 +000075std::string Replacement::toString() const {
76 std::string result;
77 llvm::raw_string_ostream stream(result);
Daniel Jasper8a999452013-05-16 10:40:07 +000078 stream << FilePath << ": " << ReplacementRange.getOffset() << ":+"
79 << ReplacementRange.getLength() << ":\"" << ReplacementText << "\"";
Manuel Klimek5d51e882012-05-30 16:04:29 +000080 return result;
81}
82
Manuel Klimekf9d4cbd2012-05-23 16:29:20 +000083bool Replacement::Less::operator()(const Replacement &R1,
84 const Replacement &R2) const {
85 if (R1.FilePath != R2.FilePath) return R1.FilePath < R2.FilePath;
Daniel Jasper8a999452013-05-16 10:40:07 +000086 if (R1.ReplacementRange.getOffset() != R2.ReplacementRange.getOffset())
87 return R1.ReplacementRange.getOffset() < R2.ReplacementRange.getOffset();
88 if (R1.ReplacementRange.getLength() != R2.ReplacementRange.getLength())
89 return R1.ReplacementRange.getLength() < R2.ReplacementRange.getLength();
Manuel Klimekf9d4cbd2012-05-23 16:29:20 +000090 return R1.ReplacementText < R2.ReplacementText;
91}
92
93void Replacement::setFromSourceLocation(SourceManager &Sources,
94 SourceLocation Start, unsigned Length,
Dmitri Gribenkocfa88f82013-01-12 19:30:44 +000095 StringRef ReplacementText) {
Manuel Klimekf9d4cbd2012-05-23 16:29:20 +000096 const std::pair<FileID, unsigned> DecomposedLocation =
97 Sources.getDecomposedLoc(Start);
98 const FileEntry *Entry = Sources.getFileEntryForID(DecomposedLocation.first);
99 this->FilePath = Entry != NULL ? Entry->getName() : InvalidLocation;
Daniel Jasper8a999452013-05-16 10:40:07 +0000100 this->ReplacementRange = Range(DecomposedLocation.second, Length);
Manuel Klimekf9d4cbd2012-05-23 16:29:20 +0000101 this->ReplacementText = ReplacementText;
102}
103
104// FIXME: This should go into the Lexer, but we need to figure out how
105// to handle ranges for refactoring in general first - there is no obvious
106// good way how to integrate this into the Lexer yet.
107static int getRangeSize(SourceManager &Sources, const CharSourceRange &Range) {
108 SourceLocation SpellingBegin = Sources.getSpellingLoc(Range.getBegin());
109 SourceLocation SpellingEnd = Sources.getSpellingLoc(Range.getEnd());
110 std::pair<FileID, unsigned> Start = Sources.getDecomposedLoc(SpellingBegin);
111 std::pair<FileID, unsigned> End = Sources.getDecomposedLoc(SpellingEnd);
112 if (Start.first != End.first) return -1;
113 if (Range.isTokenRange())
114 End.second += Lexer::MeasureTokenLength(SpellingEnd, Sources,
115 LangOptions());
116 return End.second - Start.second;
117}
118
119void Replacement::setFromSourceRange(SourceManager &Sources,
120 const CharSourceRange &Range,
Dmitri Gribenkocfa88f82013-01-12 19:30:44 +0000121 StringRef ReplacementText) {
Manuel Klimekf9d4cbd2012-05-23 16:29:20 +0000122 setFromSourceLocation(Sources, Sources.getSpellingLoc(Range.getBegin()),
123 getRangeSize(Sources, Range), ReplacementText);
124}
125
126bool applyAllReplacements(Replacements &Replaces, Rewriter &Rewrite) {
127 bool Result = true;
128 for (Replacements::const_iterator I = Replaces.begin(),
129 E = Replaces.end();
130 I != E; ++I) {
131 if (I->isApplicable()) {
132 Result = I->apply(Rewrite) && Result;
133 } else {
134 Result = false;
135 }
136 }
137 return Result;
138}
139
Daniel Jasper8a999452013-05-16 10:40:07 +0000140std::string applyAllReplacements(StringRef Code, Replacements &Replaces) {
141 FileManager Files((FileSystemOptions()));
142 DiagnosticsEngine Diagnostics(
143 IntrusiveRefCntPtr<DiagnosticIDs>(new DiagnosticIDs),
144 new DiagnosticOptions);
145 Diagnostics.setClient(new TextDiagnosticPrinter(
146 llvm::outs(), &Diagnostics.getDiagnosticOptions()));
147 SourceManager SourceMgr(Diagnostics, Files);
148 Rewriter Rewrite(SourceMgr, LangOptions());
149 llvm::MemoryBuffer *Buf = llvm::MemoryBuffer::getMemBuffer(Code, "<stdin>");
150 const clang::FileEntry *Entry =
151 Files.getVirtualFile("<stdin>", Buf->getBufferSize(), 0);
152 SourceMgr.overrideFileContents(Entry, Buf);
153 FileID ID =
154 SourceMgr.createFileID(Entry, SourceLocation(), clang::SrcMgr::C_User);
155 for (Replacements::iterator I = Replaces.begin(), E = Replaces.end(); I != E;
156 ++I) {
157 Replacement Replace("<stdin>", I->getOffset(), I->getLength(),
158 I->getReplacementText());
159 if (!Replace.apply(Rewrite))
160 return "";
161 }
162 std::string Result;
163 llvm::raw_string_ostream OS(Result);
164 Rewrite.getEditBuffer(ID).write(OS);
165 OS.flush();
166 return Result;
167}
168
Edwin Vaned088a5f2013-01-11 17:04:55 +0000169RefactoringTool::RefactoringTool(const CompilationDatabase &Compilations,
170 ArrayRef<std::string> SourcePaths)
171 : ClangTool(Compilations, SourcePaths) {}
172
173Replacements &RefactoringTool::getReplacements() { return Replace; }
174
175int RefactoringTool::runAndSave(FrontendActionFactory *ActionFactory) {
176 if (int Result = run(ActionFactory)) {
177 return Result;
178 }
179
180 LangOptions DefaultLangOptions;
181 IntrusiveRefCntPtr<DiagnosticOptions> DiagOpts = new DiagnosticOptions();
182 TextDiagnosticPrinter DiagnosticPrinter(llvm::errs(), &*DiagOpts);
183 DiagnosticsEngine Diagnostics(
Dmitri Gribenkocfa88f82013-01-12 19:30:44 +0000184 IntrusiveRefCntPtr<DiagnosticIDs>(new DiagnosticIDs()),
Edwin Vaned088a5f2013-01-11 17:04:55 +0000185 &*DiagOpts, &DiagnosticPrinter, false);
186 SourceManager Sources(Diagnostics, getFiles());
187 Rewriter Rewrite(Sources, DefaultLangOptions);
188
189 if (!applyAllReplacements(Rewrite)) {
190 llvm::errs() << "Skipped some replacements.\n";
191 }
192
193 return saveRewrittenFiles(Rewrite);
194}
195
196bool RefactoringTool::applyAllReplacements(Rewriter &Rewrite) {
197 return tooling::applyAllReplacements(Replace, Rewrite);
198}
199
200int RefactoringTool::saveRewrittenFiles(Rewriter &Rewrite) {
Manuel Klimekf9d4cbd2012-05-23 16:29:20 +0000201 for (Rewriter::buffer_iterator I = Rewrite.buffer_begin(),
202 E = Rewrite.buffer_end();
203 I != E; ++I) {
204 // FIXME: This code is copied from the FixItRewriter.cpp - I think it should
205 // go into directly into Rewriter (there we also have the Diagnostics to
206 // handle the error cases better).
207 const FileEntry *Entry =
208 Rewrite.getSourceMgr().getFileEntryForID(I->first);
209 std::string ErrorInfo;
210 llvm::raw_fd_ostream FileStream(
211 Entry->getName(), ErrorInfo, llvm::raw_fd_ostream::F_Binary);
212 if (!ErrorInfo.empty())
Edwin Vaned088a5f2013-01-11 17:04:55 +0000213 return 1;
Manuel Klimekf9d4cbd2012-05-23 16:29:20 +0000214 I->second.write(FileStream);
215 FileStream.flush();
216 }
Edwin Vaned088a5f2013-01-11 17:04:55 +0000217 return 0;
Manuel Klimekf9d4cbd2012-05-23 16:29:20 +0000218}
219
220} // end namespace tooling
221} // end namespace clang