clang-tools 6.0.0

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 static cl::opt<bool> DoFormat(
47 "format",
48 cl::desc("Enable formatting of code changed by applying replacements.\n"
49 "Use -style to choose formatting style.\n"),
50 cl::cat(FormattingCategory));
51
52 // FIXME: Consider making the default behaviour for finding a style
53 // configuration file to start the search anew for every file being changed to
54 // handle situations where the style is different for different parts of a
55 // project.
56
57 static cl::opt<std::string> FormatStyleConfig(
58 "style-config",
59 cl::desc("Path to a directory containing a .clang-format file\n"
60 "describing a formatting style to use for formatting\n"
61 "code when -style=file.\n"),
62 cl::init(""), cl::cat(FormattingCategory));
63
64 static cl::opt<std::string>
65 FormatStyleOpt("style", cl::desc(format::StyleOptionHelpDescription),
66 cl::init("LLVM"), cl::cat(FormattingCategory));
67
68 namespace {
69 // Helper object to remove the TUReplacement and TUDiagnostic (triggered by
70 // "remove-change-desc-files" command line option) when exiting current scope.
71 class ScopedFileRemover {
72 public:
73 ScopedFileRemover(const TUReplacementFiles &Files,
74 clang::DiagnosticsEngine &Diagnostics)
75 : TURFiles(Files), Diag(Diagnostics) {}
76
77 ~ScopedFileRemover() { deleteReplacementFiles(TURFiles, Diag); }
78
79 private:
80 const TUReplacementFiles &TURFiles;
81 clang::DiagnosticsEngine &Diag;
82 };
83 } // namespace
84
85 static void printVersion(raw_ostream &OS) {
86 OS << "clang-apply-replacements version " CLANG_VERSION_STRING << "\n";
87 }
88
89 /// \brief Convenience function to get rewritten content for \c Filename from
90 /// \c Rewrites.
91 ///
92 /// \pre Replacements[i].getFilePath() == Replacements[i+1].getFilePath().
93 /// \post Replacements.empty() -> Result.empty()
94 ///
95 /// \param[in] Replacements Replacements to apply
96 /// \param[in] Rewrites Rewriter to use to apply replacements.
97 /// \param[out] Result Contents of the file after applying replacements if
98 /// replacements were provided.
99 ///
100 /// \returns \parblock
101 /// \li true if all replacements were applied successfully.
102 /// \li false if at least one replacement failed to apply.
103 static bool
104 getRewrittenData(const std::vector<tooling::Replacement> &Replacements,
105 Rewriter &Rewrites, std::string &Result) {
106 if (Replacements.empty())
107 return true;
108
109 if (!applyAllReplacements(Replacements, Rewrites))
110 return false;
111
112 SourceManager &SM = Rewrites.getSourceMgr();
113 FileManager &Files = SM.getFileManager();
114
115 StringRef FileName = Replacements.begin()->getFilePath();
116 const clang::FileEntry *Entry = Files.getFile(FileName);
117 assert(Entry && "Expected an existing file");
118 FileID ID = SM.translateFile(Entry);
119 assert(ID.isValid() && "Expected a valid FileID");
120 const RewriteBuffer *Buffer = Rewrites.getRewriteBufferFor(ID);
121 Result = std::string(Buffer->begin(), Buffer->end());
122
123 return true;
124 }
125
126 /// \brief Apply \c Replacements and return the new file contents.
127 ///
128 /// \pre Replacements[i].getFilePath() == Replacements[i+1].getFilePath().
129 /// \post Replacements.empty() -> Result.empty()
130 ///
131 /// \param[in] Replacements Replacements to apply.
132 /// \param[out] Result Contents of the file after applying replacements if
133 /// replacements were provided.
134 /// \param[in] Diagnostics For diagnostic output.
135 ///
136 /// \returns \parblock
137 /// \li true if all replacements applied successfully.
138 /// \li false if at least one replacement failed to apply.
139 static bool
140 applyReplacements(const std::vector<tooling::Replacement> &Replacements,
141 std::string &Result, DiagnosticsEngine &Diagnostics) {
142 FileManager Files((FileSystemOptions()));
143 SourceManager SM(Diagnostics, Files);
144 Rewriter Rewrites(SM, LangOptions());
145
146 return getRewrittenData(Replacements, Rewrites, Result);
147 }
148
149 /// \brief Apply code formatting to all places where replacements were made.
150 ///
151 /// \pre !Replacements.empty().
152 /// \pre Replacements[i].getFilePath() == Replacements[i+1].getFilePath().
153 /// \pre Replacements[i].getOffset() <= Replacements[i+1].getOffset().
154 ///
155 /// \param[in] Replacements Replacements that were made to the file. Provided
156 /// to indicate where changes were made.
157 /// \param[in] FileData The contents of the file \b after \c Replacements have
158 /// been applied.
159 /// \param[out] FormattedFileData The contents of the file after reformatting.
160 /// \param[in] FormatStyle Style to apply.
161 /// \param[in] Diagnostics For diagnostic output.
162 ///
163 /// \returns \parblock
164 /// \li true if reformatting replacements were all successfully
165 /// applied.
166 /// \li false if at least one reformatting replacement failed to apply.
167 static bool
168 applyFormatting(const std::vector<tooling::Replacement> &Replacements,
169 const StringRef FileData, std::string &FormattedFileData,
170 const format::FormatStyle &FormatStyle,
171 DiagnosticsEngine &Diagnostics) {
172 assert(!Replacements.empty() && "Need at least one replacement");
173
174 RangeVector Ranges = calculateChangedRanges(Replacements);
175
176 StringRef FileName = Replacements.begin()->getFilePath();
177 tooling::Replacements R =
178 format::reformat(FormatStyle, FileData, Ranges, FileName);
179
180 // FIXME: Remove this copy when tooling::Replacements is implemented as a
181 // vector instead of a set.
182 std::vector<tooling::Replacement> FormattingReplacements;
183 std::copy(R.begin(), R.end(), back_inserter(FormattingReplacements));
184
185 if (FormattingReplacements.empty()) {
186 FormattedFileData = FileData;
187 return true;
188 }
189
190 FileManager Files((FileSystemOptions()));
191 SourceManager SM(Diagnostics, Files);
192 SM.overrideFileContents(Files.getFile(FileName),
193 llvm::MemoryBuffer::getMemBufferCopy(FileData));
194 Rewriter Rewrites(SM, LangOptions());
195
196 return getRewrittenData(FormattingReplacements, Rewrites, FormattedFileData);
197 }
198
199 int main(int argc, char **argv) {
200 cl::HideUnrelatedOptions(makeArrayRef(VisibleCategories));
201
202 cl::SetVersionPrinter(printVersion);
203 cl::ParseCommandLineOptions(argc, argv);
204
205 IntrusiveRefCntPtr<DiagnosticOptions> DiagOpts(new DiagnosticOptions());
206 DiagnosticsEngine Diagnostics(
207 IntrusiveRefCntPtr<DiagnosticIDs>(new DiagnosticIDs()), DiagOpts.get());
208
209 // Determine a formatting style from options.
210 format::FormatStyle FormatStyle;
211 if (DoFormat) {
212 auto FormatStyleOrError =
213 format::getStyle(FormatStyleOpt, FormatStyleConfig, "LLVM");
214 if (!FormatStyleOrError) {
215 llvm::errs() << llvm::toString(FormatStyleOrError.takeError()) << "\n";
216 return 1;
217 }
218 FormatStyle = *FormatStyleOrError;
219 }
220
221 TUReplacements TURs;
222 TUReplacementFiles TUFiles;
223
224 std::error_code ErrorCode =
225 collectReplacementsFromDirectory(Directory, TURs, TUFiles, Diagnostics);
226
227 TUDiagnostics TUDs;
228 TUFiles.clear();
229 ErrorCode =
230 collectReplacementsFromDirectory(Directory, TUDs, TUFiles, Diagnostics);
231
232 if (ErrorCode) {
233 errs() << "Trouble iterating over directory '" << Directory
234 << "': " << ErrorCode.message() << "\n";
235 return 1;
236 }
237
238 // Remove the TUReplacementFiles (triggered by "remove-change-desc-files"
239 // command line option) when exiting main().
240 std::unique_ptr<ScopedFileRemover> Remover;
241 if (RemoveTUReplacementFiles)
242 Remover.reset(new ScopedFileRemover(TUFiles, Diagnostics));
243
244 FileManager Files((FileSystemOptions()));
245 SourceManager SM(Diagnostics, Files);
246
247 FileToReplacementsMap GroupedReplacements;
248 if (!mergeAndDeduplicate(TURs, GroupedReplacements, SM))
249 return 1;
250 if (!mergeAndDeduplicate(TUDs, GroupedReplacements, SM))
251 return 1;
252
253 Rewriter ReplacementsRewriter(SM, LangOptions());
254
255 for (const auto &FileAndReplacements : GroupedReplacements) {
256 // This shouldn't happen but if a file somehow has no replacements skip to
257 // next file.
258 if (FileAndReplacements.second.empty())
259 continue;
260
261 std::string NewFileData;
262 StringRef FileName = FileAndReplacements.first->getName();
263 if (!applyReplacements(FileAndReplacements.second, NewFileData,
264 Diagnostics)) {
265 errs() << "Failed to apply replacements to " << FileName << "\n";
266 continue;
267 }
268
269 // Apply formatting if requested.
270 if (DoFormat &&
271 !applyFormatting(FileAndReplacements.second, NewFileData, NewFileData,
272 FormatStyle, Diagnostics)) {
273 errs() << "Failed to apply reformatting replacements for " << FileName
274 << "\n";
275 continue;
276 }
277
278 // Write new file to disk
279 std::error_code EC;
280 llvm::raw_fd_ostream FileStream(FileName, EC, llvm::sys::fs::F_None);
281 if (EC) {
282 llvm::errs() << "Could not open " << FileName << " for writing\n";
283 continue;
284 }
285
286 FileStream << NewFileData;
287 }
288
289 return 0;
290 }
llvm
Definition: ClangTidyOptions.cpp:31
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:53
clang::replace::deleteReplacementFiles
bool deleteReplacementFiles(const TUReplacementFiles &Files, clang::DiagnosticsEngine &Diagnostics)
Delete the replacement files.
Definition: ApplyReplacements.cpp:383
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:168
FormattingCategory
static cl::OptionCategory FormattingCategory("Formatting Options")
clang::replace::TUReplacements
std::vector< clang::tooling::TranslationUnitReplacements > TUReplacements
Collection of TranslationUnitReplacements.
Definition: ApplyReplacements.h:42
clang::tidy::cppcoreguidelines::toString
static llvm::StringRef toString(SpecialMemberFunctionsCheck::SpecialMemberFunctionKind K)
Definition: SpecialMemberFunctionsCheck.cpp:59
RemoveTUReplacementFiles
static cl::opt< bool > RemoveTUReplacementFiles("remove-change-desc-files", cl::desc("Remove the change description files regardless of successful\ "merging/replacing."), cl::init(false), cl::cat(ReplacementCategory))
Directory
static cl::opt< std::string > Directory(cl::Positional, cl::Required, cl::desc("<Search Root Directory>"))
printVersion
static void printVersion(raw_ostream &OS)
Definition: ClangApplyReplacementsMain.cpp:85
clang::replace::RangeVector
std::vector< clang::tooling::Range > RangeVector
Collection of source ranges.
Definition: ApplyReplacements.h:39
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:348
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))
DoFormat
static cl::opt< bool > DoFormat("format", cl::desc("Enable formatting of code changed by applying replacements.\ "Use -style to choose formatting style.\"), cl::cat(FormattingCategory))
clang::replace::collectReplacementsFromDirectory
std::error_code collectReplacementsFromDirectory(const llvm::StringRef Directory, TUReplacements &TUs, TUReplacementFiles &TUFiles, clang::DiagnosticsEngine &Diagnostics)
Recursively descends through a directory structure rooted at Directory and attempts to deserialize *...
Definition: ApplyReplacements.cpp:39
VisibleCategories
const cl::OptionCategory * VisibleCategories[]
Definition: ClangApplyReplacementsMain.cpp:37
clang
Definition: AndroidTidyModule.cpp:28
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:104
main
int main(int argc, char **argv)
Definition: ClangApplyReplacementsMain.cpp:199
ApplyReplacements.h
This file provides the interface for deduplicating, detecting conflicts in, and applying collections ...
clang::replace::TUDiagnostics
std::vector< clang::tooling::TranslationUnitDiagnostics > TUDiagnostics
Collection of TranslationUniDiagnostics.
Definition: ApplyReplacements.h:48
FormatStyleConfig
static cl::opt< std::string > FormatStyleConfig("style-config", cl::desc("Path to a directory containing a .clang-format file\ "describing a formatting style to use for formatting\" "code when -style=file.\"), cl::init(""), cl::cat(FormattingCategory))
clang::replace::TUReplacementFiles
std::vector< std::string > TUReplacementFiles
Collection of TranslationUnitReplacement files.
Definition: ApplyReplacements.h:45
clang::replace::applyAllReplacements
bool applyAllReplacements(const std::vector< tooling::Replacement > &Replaces, Rewriter &Rewrite)
Definition: ApplyReplacements.cpp:170
clang::replace
Definition: ApplyReplacements.h:36
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:281
Entry
Definition: Modularize.cpp:430
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:140
FormatStyle
static cl::opt< std::string > FormatStyle("format-style", cl::desc(R"( Style for formatting code around applied fixes: - 'none' (default) turns off formatting - 'file' (literally 'file', not a placeholder) uses .clang-format file in the closest parent directory - '{ <json> }' specifies options inline, e.g. -format-style='{BasedOnStyle: llvm, IndentWidth: 8}' - 'llvm', 'google', 'webkit', 'mozilla' See clang-format documentation for the up-to-date information about formatting styles and options. This option overrides the 'FormatStyle` option in .clang-tidy file, if any. )"), cl::init("none"), cl::cat(ClangTidyCategory))