clang-tools 3.8.0

- Main Page
- Namespaces
- Classes
- Files

- File List
- File Members

ClangApplyReplacementsMain.cpp
Go to the documentation of this file.
1 //===-- ClangApplyReplacementsMain.cpp - Main file for the tool -----------===//
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 /// \file
11 /// \brief This file provides the main function for the
12 /// clang-apply-replacements tool.
13 ///
14 //===----------------------------------------------------------------------===//
15
16 #include "clang-apply-replacements/Tooling/ApplyReplacements.h"
17 #include "clang/Basic/Diagnostic.h"
18 #include "clang/Basic/DiagnosticOptions.h"
19 #include "clang/Basic/SourceManager.h"
20 #include "clang/Basic/Version.h"
21 #include "clang/Format/Format.h"
22 #include "clang/Rewrite/Core/Rewriter.h"
23 #include "llvm/ADT/STLExtras.h"
24 #include "llvm/ADT/StringSet.h"
25 #include "llvm/Support/CommandLine.h"
26
27 using namespace llvm;
28 using namespace clang;
29 using namespace clang::replace;
30
31 static cl::opt<std::string> Directory(cl::Positional, cl::Required,
32 cl::desc("<Search Root Directory>"));
33
34 static cl::OptionCategory ReplacementCategory("Replacement Options");
35 static cl::OptionCategory FormattingCategory("Formatting Options");
36
37 const cl::OptionCategory *VisibleCategories[] = {&ReplacementCategory,
38 &FormattingCategory};
39
40 static cl::opt<bool> RemoveTUReplacementFiles(
41 "remove-change-desc-files",
42 cl::desc("Remove the change description files regardless of successful\n"
43 "merging/replacing."),
44 cl::init(false), cl::cat(ReplacementCategory));
45
46
47 static 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
58 static 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
65 static cl::opt<std::string>
66 FormatStyleOpt("style", cl::desc(format::StyleOptionHelpDescription),
67 cl::init("LLVM"), cl::cat(FormattingCategory));
68
69 namespace {
70 // Helper object to remove the TUReplacement files (triggered by
71 // "remove-change-desc-files" command line option) when exiting current scope.
72 class ScopedFileRemover {
73 public:
74 ScopedFileRemover(const TUReplacementFiles &Files,
75 clang::DiagnosticsEngine &Diagnostics)
76 : TURFiles(Files), Diag(Diagnostics) {}
77
78 ~ScopedFileRemover() {
79 deleteReplacementFiles(TURFiles, Diag);
80 }
81
82 private:
83 const TUReplacementFiles &TURFiles;
84 clang::DiagnosticsEngine &Diag;
85 };
86 } // namespace
87
88 static void printVersion() {
89 outs() << "clang-apply-replacements version " CLANG_VERSION_STRING << "\n";
90 }
91
92 /// \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 ///
103 /// \returns \parblock
104 /// \li true if all replacements were applied successfully.
105 /// \li false if at least one replacement failed to apply.
106 static bool
107 getRewrittenData(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);
121 assert(ID.isValid() && "Expected a valid FileID");
122 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 ///
138 /// \returns \parblock
139 /// \li true if all replacements applied successfully.
140 /// \li false if at least one replacement failed to apply.
141 static bool
142 applyReplacements(const std::vector<tooling::Replacement> &Replacements,
143 std::string &Result, DiagnosticsEngine &Diagnostics) {
144 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.
162 /// \param[in] FormatStyle Style to apply.
163 /// \param[in] Diagnostics For diagnostic output.
164 ///
165 /// \returns \parblock
166 /// \li true if reformatting replacements were all successfully
167 /// applied.
168 /// \li false if at least one reformatting replacement failed to apply.
169 static bool
170 applyFormatting(const std::vector<tooling::Replacement> &Replacements,
171 const StringRef FileData, std::string &FormattedFileData,
172 const format::FormatStyle &FormatStyle,
173 DiagnosticsEngine &Diagnostics) {
174 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);
194 SM.overrideFileContents(Files.getFile(FileName),
195 llvm::MemoryBuffer::getMemBufferCopy(FileData));
196 Rewriter Rewrites(SM, LangOptions());
197
198 return getRewrittenData(FormattingReplacements, Rewrites, FormattedFileData);
199 }
200
201 int main(int argc, char **argv) {
202 cl::HideUnrelatedOptions(makeArrayRef(VisibleCategories));
203
204 cl::SetVersionPrinter(&printVersion);
205 cl::ParseCommandLineOptions(argc, argv);
206
207 IntrusiveRefCntPtr<DiagnosticOptions> DiagOpts(new DiagnosticOptions());
208 DiagnosticsEngine Diagnostics(
209 IntrusiveRefCntPtr<DiagnosticIDs>(new DiagnosticIDs()),
210 DiagOpts.get());
211
212 // Determine a formatting style from options.
213 format::FormatStyle FormatStyle;
214 if (DoFormat)
215 FormatStyle = format::getStyle(FormatStyleOpt, FormatStyleConfig, "LLVM");
216
217 TUReplacements TUs;
218 TUReplacementFiles TURFiles;
219
220 std::error_code ErrorCode =
221 collectReplacementsFromDirectory(Directory, TUs, TURFiles, Diagnostics);
222
223 if (ErrorCode) {
224 errs() << "Trouble iterating over directory '" << Directory
225 << "': " << ErrorCode.message() << "\n";
226 return 1;
227 }
228
229 // Remove the TUReplacementFiles (triggered by "remove-change-desc-files"
230 // command line option) when exiting main().
231 std::unique_ptr<ScopedFileRemover> Remover;
232 if (RemoveTUReplacementFiles)
233 Remover.reset(new ScopedFileRemover(TURFiles, Diagnostics));
234
235 FileManager Files((FileSystemOptions()));
236 SourceManager SM(Diagnostics, Files);
237
238 FileToReplacementsMap GroupedReplacements;
239 if (!mergeAndDeduplicate(TUs, GroupedReplacements, SM))
240 return 1;
241
242 Rewriter ReplacementsRewriter(SM, LangOptions());
243
244 for (const auto &FileAndReplacements : GroupedReplacements) {
245 // This shouldn't happen but if a file somehow has no replacements skip to
246 // next file.
247 if (FileAndReplacements.second.empty())
248 continue;
249
250 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";
255 continue;
256 }
257
258 // Apply formatting if requested.
259 if (DoFormat &&
260 !applyFormatting(FileAndReplacements.second, NewFileData, NewFileData,
261 FormatStyle, Diagnostics)) {
262 errs() << "Failed to apply reformatting replacements for " << FileName
263 << "\n";
264 continue;
265 }
266
267 // Write new file to disk
268 std::error_code EC;
269 llvm::raw_fd_ostream FileStream(FileName, EC, llvm::sys::fs::F_None);
270 if (EC) {
271 llvm::errs() << "Could not open " << FileName << " for writing\n";
272 continue;
273 }
274
275 FileStream << NewFileData;
276 }
277
278 return 0;
279 }
clang::replace::FileToReplacementsMap
llvm::DenseMap< const clang::FileEntry *, std::vector< clang::tooling::Replacement > > FileToReplacementsMap
Map mapping file name to Replacements targeting that file.
Definition: ApplyReplacements.h:50
clang::replace::deleteReplacementFiles
bool deleteReplacementFiles(const TUReplacementFiles &Files, clang::DiagnosticsEngine &Diagnostics)
Delete the replacement files.
Definition: ApplyReplacements.cpp:243
applyFormatting
static bool applyFormatting(const std::vector< tooling::Replacement > &Replacements, const StringRef FileData, std::string &FormattedFileData, const format::FormatStyle &FormatStyle, DiagnosticsEngine &Diagnostics)
Apply code formatting to all places where replacements were made.
Definition: ClangApplyReplacementsMain.cpp:170
FormatStyleConfig
static cl::opt< std::string > FormatStyleConfig("style-config", cl::desc("Path to a directory containing a .clang-format file\n""describing a formatting style to use for formatting\n""code when -style=file.\n"), cl::init(""), cl::cat(FormattingCategory))
FormattingCategory
static cl::OptionCategory FormattingCategory("Formatting Options")
clang::replace::TUReplacements
std::vector< clang::tooling::TranslationUnitReplacements > TUReplacements
Collection of TranslationUnitReplacements.
Definition: ApplyReplacements.h:42
printVersion
static void printVersion()
Definition: ClangApplyReplacementsMain.cpp:88
clang::replace::collectReplacementsFromDirectory
std::error_code collectReplacementsFromDirectory(const llvm::StringRef Directory, TUReplacements &TUs, TUReplacementFiles &TURFiles, clang::DiagnosticsEngine &Diagnostics)
Recursively descends through a directory structure rooted at Directory and attempts to deserialize *...
Definition: ApplyReplacements.cpp:40
Directory
static cl::opt< std::string > Directory(cl::Positional, cl::Required, cl::desc("<Search Root Directory>"))
SM
SourceManager & SM
Definition: IncludeOrderCheck.cpp:44
clang::replace::RangeVector
std::vector< clang::tooling::Range > RangeVector
Collection of source ranges.
Definition: ApplyReplacements.h:38
clang::replace::calculateChangedRanges
RangeVector calculateChangedRanges(const std::vector< clang::tooling::Replacement > &Replacements)
Given a collection of Replacements for a single file, produces a list of source ranges that enclose t...
Definition: ApplyReplacements.cpp:207
RemoveTUReplacementFiles
static cl::opt< bool > RemoveTUReplacementFiles("remove-change-desc-files", cl::desc("Remove the change description files regardless of successful\n""merging/replacing."), cl::init(false), cl::cat(ReplacementCategory))
ReplacementCategory
static cl::OptionCategory ReplacementCategory("Replacement Options")
FormatStyleOpt
static cl::opt< std::string > FormatStyleOpt("style", cl::desc(format::StyleOptionHelpDescription), cl::init("LLVM"), cl::cat(FormattingCategory))
VisibleCategories
const cl::OptionCategory * VisibleCategories[]
Definition: ClangApplyReplacementsMain.cpp:37
getRewrittenData
static bool getRewrittenData(const std::vector< tooling::Replacement > &Replacements, Rewriter &Rewrites, std::string &Result)
Convenience function to get rewritten content for Filename from Rewrites.
Definition: ClangApplyReplacementsMain.cpp:107
DiagOpts
IntrusiveRefCntPtr< DiagnosticOptions > DiagOpts
Definition: ClangTidy.cpp:169
Files
FileManager Files
Definition: ClangTidy.cpp:167
main
int main(int argc, char **argv)
Definition: ClangApplyReplacementsMain.cpp:201
ApplyReplacements.h
This file provides the interface for deduplicating, detecting conflicts in, and applying collections ...
DoFormat
static cl::opt< bool > DoFormat("format", cl::desc("Enable formatting of code changed by applying replacements.\n""Use -style to choose formatting style.\n"), cl::cat(FormattingCategory))
clang::replace::TUReplacementFiles
std::vector< std::string > TUReplacementFiles
Collection of TranslationUnitReplacement files.
Definition: ApplyReplacements.h:45
clang::replace::mergeAndDeduplicate
bool mergeAndDeduplicate(const TUReplacements &TUs, FileToReplacementsMap &GroupedReplacements, clang::SourceManager &SM)
Deduplicate, check for conflicts, and apply all Replacements stored in TUs.
Definition: ApplyReplacements.cpp:166
Entry
Definition: Modularize.cpp:432
Result
const NamedDecl * Result
Definition: USRFinder.cpp:121
applyReplacements
static bool applyReplacements(const std::vector< tooling::Replacement > &Replacements, std::string &Result, DiagnosticsEngine &Diagnostics)
Apply Replacements and return the new file contents.
Definition: ClangApplyReplacementsMain.cpp:142