blob: 5bc06bb1e62ed54d1cf356082a9c3178a8653ac5 [file] [log] [blame]
Edwin Vaneb225be22013-09-03 17:58:19 +00001//===-- ClangApplyReplacementsMain.cpp - Main file for the tool -----------===//
Edwin Vane3bf14ce2013-08-22 13:07:14 +00002//
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/// \file
Edwin Vaneb225be22013-09-03 17:58:19 +000011/// \brief This file provides the main function for the
12/// clang-apply-replacements tool.
Edwin Vane3bf14ce2013-08-22 13:07:14 +000013///
14//===----------------------------------------------------------------------===//
15
Edwin Vaneb225be22013-09-03 17:58:19 +000016#include "clang-apply-replacements/Tooling/ApplyReplacements.h"
Edwin Vane3bf14ce2013-08-22 13:07:14 +000017#include "clang/Basic/Diagnostic.h"
18#include "clang/Basic/DiagnosticOptions.h"
Edwin Vane1106ef22013-08-22 13:40:32 +000019#include "clang/Basic/SourceManager.h"
Edwin Vane6d5350c2013-09-24 18:14:54 +000020#include "clang/Basic/Version.h"
Edwin Vane59d93af2013-09-30 13:59:21 +000021#include "clang/Format/Format.h"
Edwin Vanef18633c2013-08-28 17:19:10 +000022#include "clang/Rewrite/Core/Rewriter.h"
Edwin Vane6d5350c2013-09-24 18:14:54 +000023#include "llvm/ADT/STLExtras.h"
24#include "llvm/ADT/StringSet.h"
Edwin Vane3bf14ce2013-08-22 13:07:14 +000025#include "llvm/Support/CommandLine.h"
26
27using namespace llvm;
28using namespace clang;
29using namespace clang::replace;
30
31static cl::opt<std::string> Directory(cl::Positional, cl::Required,
32 cl::desc("<Search Root Directory>"));
33
Chris Bienemanfc4fd522015-01-28 22:45:26 +000034static cl::OptionCategory ReplacementCategory("Replacement Options");
Edwin Vane59d93af2013-09-30 13:59:21 +000035static cl::OptionCategory FormattingCategory("Formatting Options");
36
Chris Bienemanfc4fd522015-01-28 22:45:26 +000037const cl::OptionCategory *VisibleCategories[] = {&ReplacementCategory,
38 &FormattingCategory};
39
Tareq A. Siraj11755522013-08-26 19:58:59 +000040static cl::opt<bool> RemoveTUReplacementFiles(
41 "remove-change-desc-files",
42 cl::desc("Remove the change description files regardless of successful\n"
43 "merging/replacing."),
Chris Bienemanfc4fd522015-01-28 22:45:26 +000044 cl::init(false), cl::cat(ReplacementCategory));
Tareq A. Siraj11755522013-08-26 19:58:59 +000045
Edwin Vane59d93af2013-09-30 13:59:21 +000046
47static cl::opt<bool> DoFormat(
48 "format",
49 cl::desc("Enable formatting of code changed by applying replacements.\n"
50 "Use -style to choose formatting style.\n"),
51 cl::cat(FormattingCategory));
52
53// FIXME: Consider making the default behaviour for finding a style
54// configuration file to start the search anew for every file being changed to
55// handle situations where the style is different for different parts of a
56// project.
57
58static cl::opt<std::string> FormatStyleConfig(
59 "style-config",
60 cl::desc("Path to a directory containing a .clang-format file\n"
61 "describing a formatting style to use for formatting\n"
62 "code when -style=file.\n"),
63 cl::init(""), cl::cat(FormattingCategory));
64
65static cl::opt<std::string>
66FormatStyleOpt("style", cl::desc(format::StyleOptionHelpDescription),
67 cl::init("LLVM"), cl::cat(FormattingCategory));
Edwin Vane6d5350c2013-09-24 18:14:54 +000068
Benjamin Kramere7103712015-03-23 12:49:15 +000069namespace {
Tareq A. Siraj11755522013-08-26 19:58:59 +000070// Helper object to remove the TUReplacement files (triggered by
71// "remove-change-desc-files" command line option) when exiting current scope.
72class ScopedFileRemover {
73public:
74 ScopedFileRemover(const TUReplacementFiles &Files,
75 clang::DiagnosticsEngine &Diagnostics)
76 : TURFiles(Files), Diag(Diagnostics) {}
77
78 ~ScopedFileRemover() {
79 deleteReplacementFiles(TURFiles, Diag);
80 }
81
82private:
83 const TUReplacementFiles &TURFiles;
84 clang::DiagnosticsEngine &Diag;
85};
Benjamin Kramere7103712015-03-23 12:49:15 +000086} // namespace
Tareq A. Siraj11755522013-08-26 19:58:59 +000087
Benjamin Kramere7103712015-03-23 12:49:15 +000088static void printVersion() {
Edwin Vane6d5350c2013-09-24 18:14:54 +000089 outs() << "clang-apply-replacements version " CLANG_VERSION_STRING << "\n";
90}
91
Edwin Vane59d93af2013-09-30 13:59:21 +000092/// \brief Convenience function to get rewritten content for \c Filename from
93/// \c Rewrites.
94///
95/// \pre Replacements[i].getFilePath() == Replacements[i+1].getFilePath().
96/// \post Replacements.empty() -> Result.empty()
97///
98/// \param[in] Replacements Replacements to apply
99/// \param[in] Rewrites Rewriter to use to apply replacements.
100/// \param[out] Result Contents of the file after applying replacements if
101/// replacements were provided.
102///
NAKAMURA Takumie677e2f2015-09-19 02:21:28 +0000103/// \returns \parblock
104/// \li true if all replacements were applied successfully.
Edwin Vane59d93af2013-09-30 13:59:21 +0000105/// \li false if at least one replacement failed to apply.
106static bool
107getRewrittenData(const std::vector<tooling::Replacement> &Replacements,
108 Rewriter &Rewrites, std::string &Result) {
109 if (Replacements.empty()) return true;
110
111 if (!tooling::applyAllReplacements(Replacements, Rewrites))
112 return false;
113
114 SourceManager &SM = Rewrites.getSourceMgr();
115 FileManager &Files = SM.getFileManager();
116
117 StringRef FileName = Replacements.begin()->getFilePath();
118 const clang::FileEntry *Entry = Files.getFile(FileName);
119 assert(Entry && "Expected an existing file");
120 FileID ID = SM.translateFile(Entry);
Yaron Keren8b563662015-10-03 10:46:20 +0000121 assert(ID.isValid() && "Expected a valid FileID");
Edwin Vane59d93af2013-09-30 13:59:21 +0000122 const RewriteBuffer *Buffer = Rewrites.getRewriteBufferFor(ID);
123 Result = std::string(Buffer->begin(), Buffer->end());
124
125 return true;
126}
127
128/// \brief Apply \c Replacements and return the new file contents.
129///
130/// \pre Replacements[i].getFilePath() == Replacements[i+1].getFilePath().
131/// \post Replacements.empty() -> Result.empty()
132///
133/// \param[in] Replacements Replacements to apply.
134/// \param[out] Result Contents of the file after applying replacements if
135/// replacements were provided.
136/// \param[in] Diagnostics For diagnostic output.
137///
NAKAMURA Takumie677e2f2015-09-19 02:21:28 +0000138/// \returns \parblock
139/// \li true if all replacements applied successfully.
Edwin Vane59d93af2013-09-30 13:59:21 +0000140/// \li false if at least one replacement failed to apply.
Benjamin Kramere7103712015-03-23 12:49:15 +0000141static bool
142applyReplacements(const std::vector<tooling::Replacement> &Replacements,
143 std::string &Result, DiagnosticsEngine &Diagnostics) {
Edwin Vane59d93af2013-09-30 13:59:21 +0000144 FileManager Files((FileSystemOptions()));
145 SourceManager SM(Diagnostics, Files);
146 Rewriter Rewrites(SM, LangOptions());
147
148 return getRewrittenData(Replacements, Rewrites, Result);
149}
150
151/// \brief Apply code formatting to all places where replacements were made.
152///
153/// \pre !Replacements.empty().
154/// \pre Replacements[i].getFilePath() == Replacements[i+1].getFilePath().
155/// \pre Replacements[i].getOffset() <= Replacements[i+1].getOffset().
156///
157/// \param[in] Replacements Replacements that were made to the file. Provided
158/// to indicate where changes were made.
159/// \param[in] FileData The contents of the file \b after \c Replacements have
160/// been applied.
161/// \param[out] FormattedFileData The contents of the file after reformatting.
Edwin Vane2fb10d12013-10-01 13:21:39 +0000162/// \param[in] FormatStyle Style to apply.
Edwin Vane59d93af2013-09-30 13:59:21 +0000163/// \param[in] Diagnostics For diagnostic output.
164///
NAKAMURA Takumie677e2f2015-09-19 02:21:28 +0000165/// \returns \parblock
166/// \li true if reformatting replacements were all successfully
Edwin Vane59d93af2013-09-30 13:59:21 +0000167/// applied.
168/// \li false if at least one reformatting replacement failed to apply.
Benjamin Kramere7103712015-03-23 12:49:15 +0000169static bool
170applyFormatting(const std::vector<tooling::Replacement> &Replacements,
171 const StringRef FileData, std::string &FormattedFileData,
172 const format::FormatStyle &FormatStyle,
173 DiagnosticsEngine &Diagnostics) {
Edwin Vane59d93af2013-09-30 13:59:21 +0000174 assert(!Replacements.empty() && "Need at least one replacement");
175
176 RangeVector Ranges = calculateChangedRanges(Replacements);
177
178 StringRef FileName = Replacements.begin()->getFilePath();
179 tooling::Replacements R =
180 format::reformat(FormatStyle, FileData, Ranges, FileName);
181
182 // FIXME: Remove this copy when tooling::Replacements is implemented as a
183 // vector instead of a set.
184 std::vector<tooling::Replacement> FormattingReplacements;
185 std::copy(R.begin(), R.end(), back_inserter(FormattingReplacements));
186
187 if (FormattingReplacements.empty()) {
188 FormattedFileData = FileData;
189 return true;
190 }
191
192 FileManager Files((FileSystemOptions()));
193 SourceManager SM(Diagnostics, Files);
David Blaikie715c98a2014-08-27 20:54:50 +0000194 SM.overrideFileContents(Files.getFile(FileName),
195 llvm::MemoryBuffer::getMemBufferCopy(FileData));
Edwin Vane59d93af2013-09-30 13:59:21 +0000196 Rewriter Rewrites(SM, LangOptions());
197
198 return getRewrittenData(FormattingReplacements, Rewrites, FormattedFileData);
199}
200
Edwin Vane3bf14ce2013-08-22 13:07:14 +0000201int main(int argc, char **argv) {
Chris Bienemanfc4fd522015-01-28 22:45:26 +0000202 cl::HideUnrelatedOptions(makeArrayRef(VisibleCategories));
Edwin Vane6d5350c2013-09-24 18:14:54 +0000203
204 cl::SetVersionPrinter(&printVersion);
Edwin Vane3bf14ce2013-08-22 13:07:14 +0000205 cl::ParseCommandLineOptions(argc, argv);
206
207 IntrusiveRefCntPtr<DiagnosticOptions> DiagOpts(new DiagnosticOptions());
208 DiagnosticsEngine Diagnostics(
209 IntrusiveRefCntPtr<DiagnosticIDs>(new DiagnosticIDs()),
Alp Toker573583e2014-07-05 03:04:33 +0000210 DiagOpts.get());
Edwin Vane3bf14ce2013-08-22 13:07:14 +0000211
Edwin Vane59d93af2013-09-30 13:59:21 +0000212 // Determine a formatting style from options.
213 format::FormatStyle FormatStyle;
214 if (DoFormat)
Alexander Kornienko3daaf322013-12-02 15:22:30 +0000215 FormatStyle = format::getStyle(FormatStyleOpt, FormatStyleConfig, "LLVM");
Edwin Vane59d93af2013-09-30 13:59:21 +0000216
Edwin Vane3bf14ce2013-08-22 13:07:14 +0000217 TUReplacements TUs;
Tareq A. Siraj11755522013-08-26 19:58:59 +0000218 TUReplacementFiles TURFiles;
Edwin Vane3bf14ce2013-08-22 13:07:14 +0000219
Rafael Espindolac7f0d232014-06-12 22:08:48 +0000220 std::error_code ErrorCode =
Tareq A. Siraj11755522013-08-26 19:58:59 +0000221 collectReplacementsFromDirectory(Directory, TUs, TURFiles, Diagnostics);
Edwin Vane3bf14ce2013-08-22 13:07:14 +0000222
223 if (ErrorCode) {
224 errs() << "Trouble iterating over directory '" << Directory
225 << "': " << ErrorCode.message() << "\n";
Edwin Vanec0f00b72013-10-05 12:15:58 +0000226 return 1;
Edwin Vane3bf14ce2013-08-22 13:07:14 +0000227 }
228
Tareq A. Siraj11755522013-08-26 19:58:59 +0000229 // Remove the TUReplacementFiles (triggered by "remove-change-desc-files"
230 // command line option) when exiting main().
Ahmed Charles6a2dc5c2014-03-09 09:24:40 +0000231 std::unique_ptr<ScopedFileRemover> Remover;
Tareq A. Siraj11755522013-08-26 19:58:59 +0000232 if (RemoveTUReplacementFiles)
233 Remover.reset(new ScopedFileRemover(TURFiles, Diagnostics));
234
Edwin Vane1106ef22013-08-22 13:40:32 +0000235 FileManager Files((FileSystemOptions()));
236 SourceManager SM(Diagnostics, Files);
237
Edwin Vane3bf14ce2013-08-22 13:07:14 +0000238 FileToReplacementsMap GroupedReplacements;
Edwin Vane1106ef22013-08-22 13:40:32 +0000239 if (!mergeAndDeduplicate(TUs, GroupedReplacements, SM))
240 return 1;
241
Edwin Vane59d93af2013-09-30 13:59:21 +0000242 Rewriter ReplacementsRewriter(SM, LangOptions());
Edwin Vanef18633c2013-08-28 17:19:10 +0000243
Benjamin Kramerab7cefc2014-09-09 13:53:51 +0000244 for (const auto &FileAndReplacements : GroupedReplacements) {
Edwin Vane59d93af2013-09-30 13:59:21 +0000245 // This shouldn't happen but if a file somehow has no replacements skip to
246 // next file.
Benjamin Kramerab7cefc2014-09-09 13:53:51 +0000247 if (FileAndReplacements.second.empty())
Edwin Vane59d93af2013-09-30 13:59:21 +0000248 continue;
249
Benjamin Kramerab7cefc2014-09-09 13:53:51 +0000250 std::string NewFileData;
251 const char *FileName = FileAndReplacements.first->getName();
252 if (!applyReplacements(FileAndReplacements.second, NewFileData,
253 Diagnostics)) {
254 errs() << "Failed to apply replacements to " << FileName << "\n";
Edwin Vane59d93af2013-09-30 13:59:21 +0000255 continue;
256 }
257
258 // Apply formatting if requested.
Benjamin Kramerab7cefc2014-09-09 13:53:51 +0000259 if (DoFormat &&
260 !applyFormatting(FileAndReplacements.second, NewFileData, NewFileData,
261 FormatStyle, Diagnostics)) {
262 errs() << "Failed to apply reformatting replacements for " << FileName
Edwin Vane59d93af2013-09-30 13:59:21 +0000263 << "\n";
264 continue;
265 }
266
267 // Write new file to disk
Rafael Espindolab14bd532014-08-25 18:17:00 +0000268 std::error_code EC;
Nikola Smiljanic76c21b52014-12-09 02:57:56 +0000269 llvm::raw_fd_ostream FileStream(FileName, EC, llvm::sys::fs::F_None);
Rafael Espindolab14bd532014-08-25 18:17:00 +0000270 if (EC) {
Benjamin Kramerab7cefc2014-09-09 13:53:51 +0000271 llvm::errs() << "Could not open " << FileName << " for writing\n";
Edwin Vane59d93af2013-09-30 13:59:21 +0000272 continue;
273 }
274
275 FileStream << NewFileData;
276 }
Edwin Vanef18633c2013-08-28 17:19:10 +0000277
278 return 0;
Edwin Vane3bf14ce2013-08-22 13:07:14 +0000279}