blob: 950b80062cd953a4675e73b86020ea73d2af5ff6 [file] [log] [blame]
Alex Lorenzb54ef6a2017-09-14 10:06:52 +00001//===--- ClangRefactor.cpp - Clang-based refactoring 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 implements a clang-refactor tool that performs various
12/// source transformations.
13///
14//===----------------------------------------------------------------------===//
15
16#include "TestSupport.h"
Alex Lorenze1b7b952017-10-16 17:31:16 +000017#include "clang/Frontend/CommandLineSourceLoc.h"
Alex Lorenzf5ca27c2017-10-16 18:28:26 +000018#include "clang/Frontend/TextDiagnosticPrinter.h"
Alex Lorenzb54ef6a2017-09-14 10:06:52 +000019#include "clang/Rewrite/Core/Rewriter.h"
Alex Lorenz3d712c42017-09-14 13:16:14 +000020#include "clang/Tooling/CommonOptionsParser.h"
Alex Lorenzb54ef6a2017-09-14 10:06:52 +000021#include "clang/Tooling/Refactoring.h"
22#include "clang/Tooling/Refactoring/RefactoringAction.h"
Alex Lorenzad38fbf2017-10-13 01:53:13 +000023#include "clang/Tooling/Refactoring/RefactoringOptions.h"
Alex Lorenzb54ef6a2017-09-14 10:06:52 +000024#include "clang/Tooling/Refactoring/Rename/RenamingAction.h"
25#include "clang/Tooling/Tooling.h"
26#include "llvm/Support/CommandLine.h"
27#include "llvm/Support/FileSystem.h"
Eric Liuc47fd012017-11-07 14:35:03 +000028#include "llvm/Support/Signals.h"
Alex Lorenzb54ef6a2017-09-14 10:06:52 +000029#include "llvm/Support/raw_ostream.h"
30#include <string>
31
32using namespace clang;
33using namespace tooling;
34using namespace refactor;
35namespace cl = llvm::cl;
36
37namespace opts {
38
Alex Lorenzad38fbf2017-10-13 01:53:13 +000039static cl::OptionCategory CommonRefactorOptions("Refactoring options");
Alex Lorenzb54ef6a2017-09-14 10:06:52 +000040
Alex Lorenzb54ef6a2017-09-14 10:06:52 +000041static cl::opt<bool> Verbose("v", cl::desc("Use verbose output"),
Alex Lorenzad38fbf2017-10-13 01:53:13 +000042 cl::cat(cl::GeneralCategory),
Alex Lorenzb54ef6a2017-09-14 10:06:52 +000043 cl::sub(*cl::AllSubCommands));
Haojian Wu55186782017-10-20 12:37:16 +000044
45static cl::opt<bool> Inplace("i", cl::desc("Inplace edit <file>s"),
46 cl::cat(cl::GeneralCategory),
47 cl::sub(*cl::AllSubCommands));
48
Alex Lorenzb54ef6a2017-09-14 10:06:52 +000049} // end namespace opts
50
51namespace {
52
53/// Stores the parsed `-selection` argument.
54class SourceSelectionArgument {
55public:
56 virtual ~SourceSelectionArgument() {}
57
58 /// Parse the `-selection` argument.
59 ///
60 /// \returns A valid argument when the parse succedeed, null otherwise.
61 static std::unique_ptr<SourceSelectionArgument> fromString(StringRef Value);
62
63 /// Prints any additional state associated with the selection argument to
64 /// the given output stream.
Alex Lorenze1b7b952017-10-16 17:31:16 +000065 virtual void print(raw_ostream &OS) {}
Alex Lorenzb54ef6a2017-09-14 10:06:52 +000066
67 /// Returns a replacement refactoring result consumer (if any) that should
68 /// consume the results of a refactoring operation.
69 ///
70 /// The replacement refactoring result consumer is used by \c
71 /// TestSourceSelectionArgument to inject a test-specific result handling
72 /// logic into the refactoring operation. The test-specific consumer
73 /// ensures that the individual results in a particular test group are
74 /// identical.
Alex Lorenzf5ca27c2017-10-16 18:28:26 +000075 virtual std::unique_ptr<ClangRefactorToolConsumerInterface>
76 createCustomConsumer() {
Alex Lorenzb54ef6a2017-09-14 10:06:52 +000077 return nullptr;
78 }
79
80 /// Runs the give refactoring function for each specified selection.
81 ///
82 /// \returns true if an error occurred, false otherwise.
83 virtual bool
84 forAllRanges(const SourceManager &SM,
85 llvm::function_ref<void(SourceRange R)> Callback) = 0;
86};
87
88/// Stores the parsed -selection=test:<filename> option.
89class TestSourceSelectionArgument final : public SourceSelectionArgument {
90public:
91 TestSourceSelectionArgument(TestSelectionRangesInFile TestSelections)
92 : TestSelections(std::move(TestSelections)) {}
93
94 void print(raw_ostream &OS) override { TestSelections.dump(OS); }
95
Alex Lorenzf5ca27c2017-10-16 18:28:26 +000096 std::unique_ptr<ClangRefactorToolConsumerInterface>
97 createCustomConsumer() override {
Alex Lorenzb54ef6a2017-09-14 10:06:52 +000098 return TestSelections.createConsumer();
99 }
100
101 /// Testing support: invokes the selection action for each selection range in
102 /// the test file.
103 bool forAllRanges(const SourceManager &SM,
104 llvm::function_ref<void(SourceRange R)> Callback) override {
105 return TestSelections.foreachRange(SM, Callback);
106 }
107
108private:
109 TestSelectionRangesInFile TestSelections;
110};
111
Alex Lorenze1b7b952017-10-16 17:31:16 +0000112/// Stores the parsed -selection=filename:line:column[-line:column] option.
113class SourceRangeSelectionArgument final : public SourceSelectionArgument {
114public:
115 SourceRangeSelectionArgument(ParsedSourceRange Range)
116 : Range(std::move(Range)) {}
117
118 bool forAllRanges(const SourceManager &SM,
119 llvm::function_ref<void(SourceRange R)> Callback) override {
120 const FileEntry *FE = SM.getFileManager().getFile(Range.FileName);
121 FileID FID = FE ? SM.translateFile(FE) : FileID();
122 if (!FE || FID.isInvalid()) {
123 llvm::errs() << "error: -selection=" << Range.FileName
124 << ":... : given file is not in the target TU\n";
125 return true;
126 }
127
128 SourceLocation Start = SM.getMacroArgExpandedLocation(
129 SM.translateLineCol(FID, Range.Begin.first, Range.Begin.second));
130 SourceLocation End = SM.getMacroArgExpandedLocation(
131 SM.translateLineCol(FID, Range.End.first, Range.End.second));
132 if (Start.isInvalid() || End.isInvalid()) {
133 llvm::errs() << "error: -selection=" << Range.FileName << ':'
134 << Range.Begin.first << ':' << Range.Begin.second << '-'
135 << Range.End.first << ':' << Range.End.second
136 << " : invalid source location\n";
137 return true;
138 }
139 Callback(SourceRange(Start, End));
140 return false;
141 }
142
143private:
144 ParsedSourceRange Range;
145};
146
Alex Lorenzb54ef6a2017-09-14 10:06:52 +0000147std::unique_ptr<SourceSelectionArgument>
148SourceSelectionArgument::fromString(StringRef Value) {
149 if (Value.startswith("test:")) {
150 StringRef Filename = Value.drop_front(strlen("test:"));
151 Optional<TestSelectionRangesInFile> ParsedTestSelection =
152 findTestSelectionRanges(Filename);
153 if (!ParsedTestSelection)
154 return nullptr; // A parsing error was already reported.
155 return llvm::make_unique<TestSourceSelectionArgument>(
156 std::move(*ParsedTestSelection));
157 }
Alex Lorenze1b7b952017-10-16 17:31:16 +0000158 Optional<ParsedSourceRange> Range = ParsedSourceRange::fromString(Value);
159 if (Range)
160 return llvm::make_unique<SourceRangeSelectionArgument>(std::move(*Range));
Alex Lorenzb54ef6a2017-09-14 10:06:52 +0000161 llvm::errs() << "error: '-selection' option must be specified using "
162 "<file>:<line>:<column> or "
Alex Lorenze1b7b952017-10-16 17:31:16 +0000163 "<file>:<line>:<column>-<line>:<column> format\n";
Alex Lorenzb54ef6a2017-09-14 10:06:52 +0000164 return nullptr;
165}
166
Alex Lorenzad38fbf2017-10-13 01:53:13 +0000167/// A container that stores the command-line options used by a single
168/// refactoring option.
169class RefactoringActionCommandLineOptions {
170public:
171 void addStringOption(const RefactoringOption &Option,
172 std::unique_ptr<cl::opt<std::string>> CLOption) {
173 StringOptions[&Option] = std::move(CLOption);
174 }
175
176 const cl::opt<std::string> &
177 getStringOption(const RefactoringOption &Opt) const {
178 auto It = StringOptions.find(&Opt);
179 return *It->second;
180 }
181
182private:
183 llvm::DenseMap<const RefactoringOption *,
184 std::unique_ptr<cl::opt<std::string>>>
185 StringOptions;
186};
187
188/// Passes the command-line option values to the options used by a single
189/// refactoring action rule.
190class CommandLineRefactoringOptionVisitor final
191 : public RefactoringOptionVisitor {
192public:
193 CommandLineRefactoringOptionVisitor(
194 const RefactoringActionCommandLineOptions &Options)
195 : Options(Options) {}
196
197 void visit(const RefactoringOption &Opt,
198 Optional<std::string> &Value) override {
199 const cl::opt<std::string> &CLOpt = Options.getStringOption(Opt);
200 if (!CLOpt.getValue().empty()) {
201 Value = CLOpt.getValue();
202 return;
203 }
204 Value = None;
205 if (Opt.isRequired())
206 MissingRequiredOptions.push_back(&Opt);
207 }
208
209 ArrayRef<const RefactoringOption *> getMissingRequiredOptions() const {
210 return MissingRequiredOptions;
211 }
212
213private:
214 llvm::SmallVector<const RefactoringOption *, 4> MissingRequiredOptions;
215 const RefactoringActionCommandLineOptions &Options;
216};
217
218/// Creates the refactoring options used by all the rules in a single
219/// refactoring action.
220class CommandLineRefactoringOptionCreator final
221 : public RefactoringOptionVisitor {
222public:
223 CommandLineRefactoringOptionCreator(
224 cl::OptionCategory &Category, cl::SubCommand &Subcommand,
225 RefactoringActionCommandLineOptions &Options)
226 : Category(Category), Subcommand(Subcommand), Options(Options) {}
227
228 void visit(const RefactoringOption &Opt, Optional<std::string> &) override {
229 if (Visited.insert(&Opt).second)
230 Options.addStringOption(Opt, create<std::string>(Opt));
231 }
232
233private:
234 template <typename T>
235 std::unique_ptr<cl::opt<T>> create(const RefactoringOption &Opt) {
236 if (!OptionNames.insert(Opt.getName()).second)
237 llvm::report_fatal_error("Multiple identical refactoring options "
238 "specified for one refactoring action");
239 // FIXME: cl::Required can be specified when this option is present
240 // in all rules in an action.
241 return llvm::make_unique<cl::opt<T>>(
242 Opt.getName(), cl::desc(Opt.getDescription()), cl::Optional,
243 cl::cat(Category), cl::sub(Subcommand));
244 }
245
246 llvm::SmallPtrSet<const RefactoringOption *, 8> Visited;
247 llvm::StringSet<> OptionNames;
248 cl::OptionCategory &Category;
249 cl::SubCommand &Subcommand;
250 RefactoringActionCommandLineOptions &Options;
251};
252
Alex Lorenzb54ef6a2017-09-14 10:06:52 +0000253/// A subcommand that corresponds to individual refactoring action.
254class RefactoringActionSubcommand : public cl::SubCommand {
255public:
256 RefactoringActionSubcommand(std::unique_ptr<RefactoringAction> Action,
257 RefactoringActionRules ActionRules,
258 cl::OptionCategory &Category)
259 : SubCommand(Action->getCommand(), Action->getDescription()),
Haojian Wu200458f2017-11-08 08:56:56 +0000260 Action(std::move(Action)), ActionRules(std::move(ActionRules)) {
Alex Lorenzb54ef6a2017-09-14 10:06:52 +0000261 // Check if the selection option is supported.
Alex Lorenzb54ef6a2017-09-14 10:06:52 +0000262 for (const auto &Rule : this->ActionRules) {
Haojian Wu200458f2017-11-08 08:56:56 +0000263 if (Rule->hasSelectionRequirement()) {
264 Selection = llvm::make_unique<cl::opt<std::string>>(
265 "selection",
266 cl::desc(
267 "The selected source range in which the refactoring should "
268 "be initiated (<file>:<line>:<column>-<line>:<column> or "
269 "<file>:<line>:<column>)"),
270 cl::cat(Category), cl::sub(*this));
Alex Lorenzb54ef6a2017-09-14 10:06:52 +0000271 break;
Haojian Wu200458f2017-11-08 08:56:56 +0000272 }
Alex Lorenzb54ef6a2017-09-14 10:06:52 +0000273 }
Alex Lorenzad38fbf2017-10-13 01:53:13 +0000274 // Create the refactoring options.
275 for (const auto &Rule : this->ActionRules) {
276 CommandLineRefactoringOptionCreator OptionCreator(Category, *this,
277 Options);
278 Rule->visitRefactoringOptions(OptionCreator);
279 }
Alex Lorenzb54ef6a2017-09-14 10:06:52 +0000280 }
281
282 ~RefactoringActionSubcommand() { unregisterSubCommand(); }
283
284 const RefactoringActionRules &getActionRules() const { return ActionRules; }
285
Haojian Wu200458f2017-11-08 08:56:56 +0000286 /// Parses the "-selection" command-line argument.
Alex Lorenzb54ef6a2017-09-14 10:06:52 +0000287 ///
288 /// \returns true on error, false otherwise.
Haojian Wu200458f2017-11-08 08:56:56 +0000289 bool parseSelectionArgument() {
Alex Lorenzb54ef6a2017-09-14 10:06:52 +0000290 if (Selection) {
291 ParsedSelection = SourceSelectionArgument::fromString(*Selection);
292 if (!ParsedSelection)
293 return true;
294 }
295 return false;
296 }
297
298 SourceSelectionArgument *getSelection() const {
299 assert(Selection && "selection not supported!");
300 return ParsedSelection.get();
301 }
Alex Lorenzad38fbf2017-10-13 01:53:13 +0000302
303 const RefactoringActionCommandLineOptions &getOptions() const {
304 return Options;
305 }
306
Alex Lorenzb54ef6a2017-09-14 10:06:52 +0000307private:
308 std::unique_ptr<RefactoringAction> Action;
309 RefactoringActionRules ActionRules;
Alex Lorenzb54ef6a2017-09-14 10:06:52 +0000310 std::unique_ptr<cl::opt<std::string>> Selection;
311 std::unique_ptr<SourceSelectionArgument> ParsedSelection;
Alex Lorenzad38fbf2017-10-13 01:53:13 +0000312 RefactoringActionCommandLineOptions Options;
Alex Lorenzb54ef6a2017-09-14 10:06:52 +0000313};
314
Alex Lorenzf5ca27c2017-10-16 18:28:26 +0000315class ClangRefactorConsumer final : public ClangRefactorToolConsumerInterface {
Alex Lorenzb54ef6a2017-09-14 10:06:52 +0000316public:
Eric Liuc47fd012017-11-07 14:35:03 +0000317 ClangRefactorConsumer(AtomicChanges &Changes) : SourceChanges(&Changes) {}
Alex Lorenzf5ca27c2017-10-16 18:28:26 +0000318
Alex Lorenze1b7b952017-10-16 17:31:16 +0000319 void handleError(llvm::Error Err) override {
Alex Lorenzf5ca27c2017-10-16 18:28:26 +0000320 Optional<PartialDiagnosticAt> Diag = DiagnosticError::take(Err);
321 if (!Diag) {
322 llvm::errs() << llvm::toString(std::move(Err)) << "\n";
323 return;
324 }
325 llvm::cantFail(std::move(Err)); // This is a success.
326 DiagnosticBuilder DB(
327 getDiags().Report(Diag->first, Diag->second.getDiagID()));
328 Diag->second.Emit(DB);
Alex Lorenzb54ef6a2017-09-14 10:06:52 +0000329 }
330
Alex Lorenze1b7b952017-10-16 17:31:16 +0000331 void handle(AtomicChanges Changes) override {
Eric Liuc47fd012017-11-07 14:35:03 +0000332 SourceChanges->insert(SourceChanges->begin(), Changes.begin(),
333 Changes.end());
Alex Lorenze1b7b952017-10-16 17:31:16 +0000334 }
335
336 void handle(SymbolOccurrences Occurrences) override {
Alex Lorenz52843502017-10-16 18:07:16 +0000337 llvm_unreachable("symbol occurrence results are not handled yet");
Alex Lorenze1b7b952017-10-16 17:31:16 +0000338 }
339
Alex Lorenze1b7b952017-10-16 17:31:16 +0000340private:
Eric Liuc47fd012017-11-07 14:35:03 +0000341 AtomicChanges *SourceChanges;
Alex Lorenzb54ef6a2017-09-14 10:06:52 +0000342};
343
344class ClangRefactorTool {
345public:
Eric Liuc47fd012017-11-07 14:35:03 +0000346 ClangRefactorTool()
347 : SelectedSubcommand(nullptr), MatchingRule(nullptr),
348 Consumer(new ClangRefactorConsumer(Changes)), HasFailed(false) {
Alex Lorenzb54ef6a2017-09-14 10:06:52 +0000349 std::vector<std::unique_ptr<RefactoringAction>> Actions =
350 createRefactoringActions();
351
352 // Actions must have unique command names so that we can map them to one
353 // subcommand.
354 llvm::StringSet<> CommandNames;
355 for (const auto &Action : Actions) {
356 if (!CommandNames.insert(Action->getCommand()).second) {
357 llvm::errs() << "duplicate refactoring action command '"
358 << Action->getCommand() << "'!";
359 exit(1);
360 }
361 }
362
363 // Create subcommands and command-line options.
364 for (auto &Action : Actions) {
365 SubCommands.push_back(llvm::make_unique<RefactoringActionSubcommand>(
366 std::move(Action), Action->createActiveActionRules(),
367 opts::CommonRefactorOptions));
368 }
369 }
370
Eric Liuc47fd012017-11-07 14:35:03 +0000371 // Initializes the selected subcommand and refactoring rule based on the
372 // command line options.
373 llvm::Error Init() {
374 auto Subcommand = getSelectedSubcommand();
375 if (!Subcommand)
376 return Subcommand.takeError();
377 auto Rule = getMatchingRule(**Subcommand);
378 if (!Rule)
379 return Rule.takeError();
Alex Lorenzb54ef6a2017-09-14 10:06:52 +0000380
Eric Liuc47fd012017-11-07 14:35:03 +0000381 SelectedSubcommand = *Subcommand;
382 MatchingRule = *Rule;
383
384 return llvm::Error::success();
385 }
386
387 bool hasFailed() const { return HasFailed; }
388
389 using TUCallbackType = std::function<void(ASTContext &)>;
390
391 // Callback of an AST action. This invokes the matching rule on the given AST.
392 void callback(ASTContext &AST) {
393 assert(SelectedSubcommand && MatchingRule && Consumer);
394 RefactoringRuleContext Context(AST.getSourceManager());
395 Context.setASTContext(AST);
396
397 // If the selection option is test specific, we use a test-specific
398 // consumer.
399 std::unique_ptr<ClangRefactorToolConsumerInterface> TestConsumer;
Haojian Wu200458f2017-11-08 08:56:56 +0000400 bool HasSelection = MatchingRule->hasSelectionRequirement();
401 if (HasSelection)
Eric Liuc47fd012017-11-07 14:35:03 +0000402 TestConsumer = SelectedSubcommand->getSelection()->createCustomConsumer();
403 ClangRefactorToolConsumerInterface *ActiveConsumer =
404 TestConsumer ? TestConsumer.get() : Consumer.get();
405 ActiveConsumer->beginTU(AST);
Haojian Wu200458f2017-11-08 08:56:56 +0000406
407 auto InvokeRule = [&](RefactoringResultConsumer &Consumer) {
408 if (opts::Verbose)
409 logInvocation(*SelectedSubcommand, Context);
410 MatchingRule->invoke(*ActiveConsumer, Context);
411 };
412 if (HasSelection) {
Eric Liuc47fd012017-11-07 14:35:03 +0000413 assert(SelectedSubcommand->getSelection() &&
414 "Missing selection argument?");
415 if (opts::Verbose)
416 SelectedSubcommand->getSelection()->print(llvm::outs());
417 if (SelectedSubcommand->getSelection()->forAllRanges(
418 Context.getSources(), [&](SourceRange R) {
419 Context.setSelectionRange(R);
Haojian Wu200458f2017-11-08 08:56:56 +0000420 InvokeRule(*ActiveConsumer);
Eric Liuc47fd012017-11-07 14:35:03 +0000421 }))
422 HasFailed = true;
423 ActiveConsumer->endTU();
424 return;
425 }
Haojian Wu200458f2017-11-08 08:56:56 +0000426 InvokeRule(*ActiveConsumer);
Eric Liuc47fd012017-11-07 14:35:03 +0000427 ActiveConsumer->endTU();
428 }
429
430 llvm::Expected<std::unique_ptr<FrontendActionFactory>>
431 getFrontendActionFactory() {
Alex Lorenzb54ef6a2017-09-14 10:06:52 +0000432 class ToolASTConsumer : public ASTConsumer {
433 public:
434 TUCallbackType Callback;
Eric Liuc47fd012017-11-07 14:35:03 +0000435 ToolASTConsumer(TUCallbackType Callback)
436 : Callback(std::move(Callback)) {}
Alex Lorenzb54ef6a2017-09-14 10:06:52 +0000437
438 void HandleTranslationUnit(ASTContext &Context) override {
439 Callback(Context);
440 }
441 };
Eric Liuc47fd012017-11-07 14:35:03 +0000442 class ToolASTAction : public ASTFrontendAction {
Alex Lorenzb54ef6a2017-09-14 10:06:52 +0000443 public:
Eric Liuc47fd012017-11-07 14:35:03 +0000444 explicit ToolASTAction(TUCallbackType Callback)
445 : Callback(std::move(Callback)) {}
Alex Lorenzb54ef6a2017-09-14 10:06:52 +0000446
Eric Liuc47fd012017-11-07 14:35:03 +0000447 protected:
448 std::unique_ptr<clang::ASTConsumer>
449 CreateASTConsumer(clang::CompilerInstance &compiler,
450 StringRef /* dummy */) override {
451 std::unique_ptr<clang::ASTConsumer> Consumer{
452 new ToolASTConsumer(Callback)};
453 return Consumer;
Alex Lorenzb54ef6a2017-09-14 10:06:52 +0000454 }
Eric Liuc47fd012017-11-07 14:35:03 +0000455
456 private:
457 TUCallbackType Callback;
Alex Lorenzb54ef6a2017-09-14 10:06:52 +0000458 };
459
Eric Liuc47fd012017-11-07 14:35:03 +0000460 class ToolActionFactory : public FrontendActionFactory {
461 public:
462 ToolActionFactory(TUCallbackType Callback)
463 : Callback(std::move(Callback)) {}
464
465 FrontendAction *create() override { return new ToolASTAction(Callback); }
466
467 private:
468 TUCallbackType Callback;
469 };
470
471 return llvm::make_unique<ToolActionFactory>(
472 [this](ASTContext &AST) { return callback(AST); });
Alex Lorenzb54ef6a2017-09-14 10:06:52 +0000473 }
474
Eric Liuc47fd012017-11-07 14:35:03 +0000475 // FIXME(ioeric): this seems to only works for changes in a single file at
476 // this point.
477 bool applySourceChanges() {
Alex Lorenze1b7b952017-10-16 17:31:16 +0000478 std::set<std::string> Files;
Eric Liuc47fd012017-11-07 14:35:03 +0000479 for (const auto &Change : Changes)
Alex Lorenze1b7b952017-10-16 17:31:16 +0000480 Files.insert(Change.getFilePath());
481 // FIXME: Add automatic formatting support as well.
482 tooling::ApplyChangesSpec Spec;
483 // FIXME: We should probably cleanup the result by default as well.
484 Spec.Cleanup = false;
485 for (const auto &File : Files) {
486 llvm::ErrorOr<std::unique_ptr<llvm::MemoryBuffer>> BufferErr =
487 llvm::MemoryBuffer::getFile(File);
488 if (!BufferErr) {
489 llvm::errs() << "error: failed to open " << File << " for rewriting\n";
490 return true;
491 }
492 auto Result = tooling::applyAtomicChanges(File, (*BufferErr)->getBuffer(),
Eric Liuc47fd012017-11-07 14:35:03 +0000493 Changes, Spec);
Alex Lorenze1b7b952017-10-16 17:31:16 +0000494 if (!Result) {
495 llvm::errs() << toString(Result.takeError());
496 return true;
497 }
498
Haojian Wu55186782017-10-20 12:37:16 +0000499 if (opts::Inplace) {
500 std::error_code EC;
501 llvm::raw_fd_ostream OS(File, EC, llvm::sys::fs::F_Text);
502 if (EC) {
503 llvm::errs() << EC.message() << "\n";
504 return true;
505 }
506 OS << *Result;
507 continue;
Alex Lorenze1b7b952017-10-16 17:31:16 +0000508 }
Haojian Wu55186782017-10-20 12:37:16 +0000509
510 llvm::outs() << *Result;
Alex Lorenze1b7b952017-10-16 17:31:16 +0000511 }
512 return false;
513 }
514
Eric Liuc47fd012017-11-07 14:35:03 +0000515private:
516 /// Logs an individual refactoring action invocation to STDOUT.
517 void logInvocation(RefactoringActionSubcommand &Subcommand,
518 const RefactoringRuleContext &Context) {
519 llvm::outs() << "invoking action '" << Subcommand.getName() << "':\n";
520 if (Context.getSelectionRange().isValid()) {
521 SourceRange R = Context.getSelectionRange();
522 llvm::outs() << " -selection=";
523 R.getBegin().print(llvm::outs(), Context.getSources());
524 llvm::outs() << " -> ";
525 R.getEnd().print(llvm::outs(), Context.getSources());
526 llvm::outs() << "\n";
527 }
528 }
529
530 llvm::Expected<RefactoringActionRule *>
Haojian Wu200458f2017-11-08 08:56:56 +0000531 getMatchingRule(RefactoringActionSubcommand &Subcommand) {
Alex Lorenzb54ef6a2017-09-14 10:06:52 +0000532 SmallVector<RefactoringActionRule *, 4> MatchingRules;
533 llvm::StringSet<> MissingOptions;
534
Alex Lorenzb54ef6a2017-09-14 10:06:52 +0000535 for (const auto &Rule : Subcommand.getActionRules()) {
Alex Lorenzad38fbf2017-10-13 01:53:13 +0000536 CommandLineRefactoringOptionVisitor Visitor(Subcommand.getOptions());
537 Rule->visitRefactoringOptions(Visitor);
Haojian Wu200458f2017-11-08 08:56:56 +0000538 if (Visitor.getMissingRequiredOptions().empty()) {
539 if (!Rule->hasSelectionRequirement()) {
540 MatchingRules.push_back(Rule.get());
541 } else {
542 Subcommand.parseSelectionArgument();
543 if (Subcommand.getSelection()) {
544 MatchingRules.push_back(Rule.get());
545 } else {
546 MissingOptions.insert("selection");
547 }
548 }
Alex Lorenzad38fbf2017-10-13 01:53:13 +0000549 }
550 for (const RefactoringOption *Opt : Visitor.getMissingRequiredOptions())
551 MissingOptions.insert(Opt->getName());
Alex Lorenzb54ef6a2017-09-14 10:06:52 +0000552 }
553 if (MatchingRules.empty()) {
Eric Liuc47fd012017-11-07 14:35:03 +0000554 std::string Error;
555 llvm::raw_string_ostream OS(Error);
556 OS << "ERROR: '" << Subcommand.getName()
557 << "' can't be invoked with the given arguments:\n";
Alex Lorenzb54ef6a2017-09-14 10:06:52 +0000558 for (const auto &Opt : MissingOptions)
Eric Liuc47fd012017-11-07 14:35:03 +0000559 OS << " missing '-" << Opt.getKey() << "' option\n";
560 OS.flush();
561 return llvm::make_error<llvm::StringError>(
562 Error, llvm::inconvertibleErrorCode());
Alex Lorenzb54ef6a2017-09-14 10:06:52 +0000563 }
Eric Liuc47fd012017-11-07 14:35:03 +0000564 if (MatchingRules.size() != 1) {
565 return llvm::make_error<llvm::StringError>(
566 llvm::Twine("ERROR: more than one matching rule of action") +
567 Subcommand.getName() + "was found with given options.",
568 llvm::inconvertibleErrorCode());
569 }
570 return MatchingRules.front();
Alex Lorenzb54ef6a2017-09-14 10:06:52 +0000571 }
Eric Liuc47fd012017-11-07 14:35:03 +0000572 // Figure out which action is specified by the user. The user must specify the
573 // action using a command-line subcommand, e.g. the invocation `clang-refactor
574 // local-rename` corresponds to the `LocalRename` refactoring action. All
575 // subcommands must have a unique names. This allows us to figure out which
576 // refactoring action should be invoked by looking at the first subcommand
577 // that's enabled by LLVM's command-line parser.
578 llvm::Expected<RefactoringActionSubcommand *> getSelectedSubcommand() {
579 auto It = llvm::find_if(
580 SubCommands,
581 [](const std::unique_ptr<RefactoringActionSubcommand> &SubCommand) {
582 return !!(*SubCommand);
583 });
584 if (It == SubCommands.end()) {
585 std::string Error;
586 llvm::raw_string_ostream OS(Error);
587 OS << "error: no refactoring action given\n";
588 OS << "note: the following actions are supported:\n";
589 for (const auto &Subcommand : SubCommands)
590 OS.indent(2) << Subcommand->getName() << "\n";
591 OS.flush();
592 return llvm::make_error<llvm::StringError>(
593 Error, llvm::inconvertibleErrorCode());
594 }
595 RefactoringActionSubcommand *Subcommand = &(**It);
Eric Liuc47fd012017-11-07 14:35:03 +0000596 return Subcommand;
597 }
598
599 std::vector<std::unique_ptr<RefactoringActionSubcommand>> SubCommands;
600 RefactoringActionSubcommand *SelectedSubcommand;
601 RefactoringActionRule *MatchingRule;
602 std::unique_ptr<ClangRefactorToolConsumerInterface> Consumer;
603 AtomicChanges Changes;
604 bool HasFailed;
Alex Lorenzb54ef6a2017-09-14 10:06:52 +0000605};
606
607} // end anonymous namespace
608
609int main(int argc, const char **argv) {
Eric Liuc47fd012017-11-07 14:35:03 +0000610 llvm::sys::PrintStackTraceOnErrorSignal(argv[0]);
611
612 ClangRefactorTool RefactorTool;
Alex Lorenzb54ef6a2017-09-14 10:06:52 +0000613
Alex Lorenz3d712c42017-09-14 13:16:14 +0000614 CommonOptionsParser Options(
Alex Lorenzad38fbf2017-10-13 01:53:13 +0000615 argc, argv, cl::GeneralCategory, cl::ZeroOrMore,
Alex Lorenz3d712c42017-09-14 13:16:14 +0000616 "Clang-based refactoring tool for C, C++ and Objective-C");
Alex Lorenzb54ef6a2017-09-14 10:06:52 +0000617
Eric Liuc47fd012017-11-07 14:35:03 +0000618 if (auto Err = RefactorTool.Init()) {
619 llvm::errs() << llvm::toString(std::move(Err)) << "\n";
Alex Lorenzb54ef6a2017-09-14 10:06:52 +0000620 return 1;
621 }
Alex Lorenzb54ef6a2017-09-14 10:06:52 +0000622
Eric Liuc47fd012017-11-07 14:35:03 +0000623 auto ActionFactory = RefactorTool.getFrontendActionFactory();
624 if (!ActionFactory) {
625 llvm::errs() << llvm::toString(ActionFactory.takeError()) << "\n";
Alex Lorenzb54ef6a2017-09-14 10:06:52 +0000626 return 1;
Eric Liuc47fd012017-11-07 14:35:03 +0000627 }
628 ClangTool Tool(Options.getCompilations(), Options.getSourcePathList());
629 bool Failed = false;
630 if (Tool.run(ActionFactory->get()) != 0) {
631 llvm::errs() << "Failed to run refactoring action on files\n";
632 // It is possible that TUs are broken while changes are generated correctly,
633 // so we still try applying changes.
634 Failed = true;
635 }
636 return RefactorTool.applySourceChanges() || Failed ||
637 RefactorTool.hasFailed();
Alex Lorenzb54ef6a2017-09-14 10:06:52 +0000638}