blob: 523c3a6220d01fd1999d82f635596a921cd57a73 [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"
28#include "llvm/Support/raw_ostream.h"
29#include <string>
30
31using namespace clang;
32using namespace tooling;
33using namespace refactor;
34namespace cl = llvm::cl;
35
36namespace opts {
37
Alex Lorenzad38fbf2017-10-13 01:53:13 +000038static cl::OptionCategory CommonRefactorOptions("Refactoring options");
Alex Lorenzb54ef6a2017-09-14 10:06:52 +000039
Alex Lorenzb54ef6a2017-09-14 10:06:52 +000040static cl::opt<bool> Verbose("v", cl::desc("Use verbose output"),
Alex Lorenzad38fbf2017-10-13 01:53:13 +000041 cl::cat(cl::GeneralCategory),
Alex Lorenzb54ef6a2017-09-14 10:06:52 +000042 cl::sub(*cl::AllSubCommands));
Haojian Wu55186782017-10-20 12:37:16 +000043
44static cl::opt<bool> Inplace("i", cl::desc("Inplace edit <file>s"),
45 cl::cat(cl::GeneralCategory),
46 cl::sub(*cl::AllSubCommands));
47
Alex Lorenzb54ef6a2017-09-14 10:06:52 +000048} // end namespace opts
49
50namespace {
51
52/// Stores the parsed `-selection` argument.
53class SourceSelectionArgument {
54public:
55 virtual ~SourceSelectionArgument() {}
56
57 /// Parse the `-selection` argument.
58 ///
59 /// \returns A valid argument when the parse succedeed, null otherwise.
60 static std::unique_ptr<SourceSelectionArgument> fromString(StringRef Value);
61
62 /// Prints any additional state associated with the selection argument to
63 /// the given output stream.
Alex Lorenze1b7b952017-10-16 17:31:16 +000064 virtual void print(raw_ostream &OS) {}
Alex Lorenzb54ef6a2017-09-14 10:06:52 +000065
66 /// Returns a replacement refactoring result consumer (if any) that should
67 /// consume the results of a refactoring operation.
68 ///
69 /// The replacement refactoring result consumer is used by \c
70 /// TestSourceSelectionArgument to inject a test-specific result handling
71 /// logic into the refactoring operation. The test-specific consumer
72 /// ensures that the individual results in a particular test group are
73 /// identical.
Alex Lorenzf5ca27c2017-10-16 18:28:26 +000074 virtual std::unique_ptr<ClangRefactorToolConsumerInterface>
75 createCustomConsumer() {
Alex Lorenzb54ef6a2017-09-14 10:06:52 +000076 return nullptr;
77 }
78
79 /// Runs the give refactoring function for each specified selection.
80 ///
81 /// \returns true if an error occurred, false otherwise.
82 virtual bool
83 forAllRanges(const SourceManager &SM,
84 llvm::function_ref<void(SourceRange R)> Callback) = 0;
85};
86
87/// Stores the parsed -selection=test:<filename> option.
88class TestSourceSelectionArgument final : public SourceSelectionArgument {
89public:
90 TestSourceSelectionArgument(TestSelectionRangesInFile TestSelections)
91 : TestSelections(std::move(TestSelections)) {}
92
93 void print(raw_ostream &OS) override { TestSelections.dump(OS); }
94
Alex Lorenzf5ca27c2017-10-16 18:28:26 +000095 std::unique_ptr<ClangRefactorToolConsumerInterface>
96 createCustomConsumer() override {
Alex Lorenzb54ef6a2017-09-14 10:06:52 +000097 return TestSelections.createConsumer();
98 }
99
100 /// Testing support: invokes the selection action for each selection range in
101 /// the test file.
102 bool forAllRanges(const SourceManager &SM,
103 llvm::function_ref<void(SourceRange R)> Callback) override {
104 return TestSelections.foreachRange(SM, Callback);
105 }
106
107private:
108 TestSelectionRangesInFile TestSelections;
109};
110
Alex Lorenze1b7b952017-10-16 17:31:16 +0000111/// Stores the parsed -selection=filename:line:column[-line:column] option.
112class SourceRangeSelectionArgument final : public SourceSelectionArgument {
113public:
114 SourceRangeSelectionArgument(ParsedSourceRange Range)
115 : Range(std::move(Range)) {}
116
117 bool forAllRanges(const SourceManager &SM,
118 llvm::function_ref<void(SourceRange R)> Callback) override {
119 const FileEntry *FE = SM.getFileManager().getFile(Range.FileName);
120 FileID FID = FE ? SM.translateFile(FE) : FileID();
121 if (!FE || FID.isInvalid()) {
122 llvm::errs() << "error: -selection=" << Range.FileName
123 << ":... : given file is not in the target TU\n";
124 return true;
125 }
126
127 SourceLocation Start = SM.getMacroArgExpandedLocation(
128 SM.translateLineCol(FID, Range.Begin.first, Range.Begin.second));
129 SourceLocation End = SM.getMacroArgExpandedLocation(
130 SM.translateLineCol(FID, Range.End.first, Range.End.second));
131 if (Start.isInvalid() || End.isInvalid()) {
132 llvm::errs() << "error: -selection=" << Range.FileName << ':'
133 << Range.Begin.first << ':' << Range.Begin.second << '-'
134 << Range.End.first << ':' << Range.End.second
135 << " : invalid source location\n";
136 return true;
137 }
138 Callback(SourceRange(Start, End));
139 return false;
140 }
141
142private:
143 ParsedSourceRange Range;
144};
145
Alex Lorenzb54ef6a2017-09-14 10:06:52 +0000146std::unique_ptr<SourceSelectionArgument>
147SourceSelectionArgument::fromString(StringRef Value) {
148 if (Value.startswith("test:")) {
149 StringRef Filename = Value.drop_front(strlen("test:"));
150 Optional<TestSelectionRangesInFile> ParsedTestSelection =
151 findTestSelectionRanges(Filename);
152 if (!ParsedTestSelection)
153 return nullptr; // A parsing error was already reported.
154 return llvm::make_unique<TestSourceSelectionArgument>(
155 std::move(*ParsedTestSelection));
156 }
Alex Lorenze1b7b952017-10-16 17:31:16 +0000157 Optional<ParsedSourceRange> Range = ParsedSourceRange::fromString(Value);
158 if (Range)
159 return llvm::make_unique<SourceRangeSelectionArgument>(std::move(*Range));
Alex Lorenzb54ef6a2017-09-14 10:06:52 +0000160 llvm::errs() << "error: '-selection' option must be specified using "
161 "<file>:<line>:<column> or "
Alex Lorenze1b7b952017-10-16 17:31:16 +0000162 "<file>:<line>:<column>-<line>:<column> format\n";
Alex Lorenzb54ef6a2017-09-14 10:06:52 +0000163 return nullptr;
164}
165
Alex Lorenzad38fbf2017-10-13 01:53:13 +0000166/// A container that stores the command-line options used by a single
167/// refactoring option.
168class RefactoringActionCommandLineOptions {
169public:
170 void addStringOption(const RefactoringOption &Option,
171 std::unique_ptr<cl::opt<std::string>> CLOption) {
172 StringOptions[&Option] = std::move(CLOption);
173 }
174
175 const cl::opt<std::string> &
176 getStringOption(const RefactoringOption &Opt) const {
177 auto It = StringOptions.find(&Opt);
178 return *It->second;
179 }
180
181private:
182 llvm::DenseMap<const RefactoringOption *,
183 std::unique_ptr<cl::opt<std::string>>>
184 StringOptions;
185};
186
187/// Passes the command-line option values to the options used by a single
188/// refactoring action rule.
189class CommandLineRefactoringOptionVisitor final
190 : public RefactoringOptionVisitor {
191public:
192 CommandLineRefactoringOptionVisitor(
193 const RefactoringActionCommandLineOptions &Options)
194 : Options(Options) {}
195
196 void visit(const RefactoringOption &Opt,
197 Optional<std::string> &Value) override {
198 const cl::opt<std::string> &CLOpt = Options.getStringOption(Opt);
199 if (!CLOpt.getValue().empty()) {
200 Value = CLOpt.getValue();
201 return;
202 }
203 Value = None;
204 if (Opt.isRequired())
205 MissingRequiredOptions.push_back(&Opt);
206 }
207
208 ArrayRef<const RefactoringOption *> getMissingRequiredOptions() const {
209 return MissingRequiredOptions;
210 }
211
212private:
213 llvm::SmallVector<const RefactoringOption *, 4> MissingRequiredOptions;
214 const RefactoringActionCommandLineOptions &Options;
215};
216
217/// Creates the refactoring options used by all the rules in a single
218/// refactoring action.
219class CommandLineRefactoringOptionCreator final
220 : public RefactoringOptionVisitor {
221public:
222 CommandLineRefactoringOptionCreator(
223 cl::OptionCategory &Category, cl::SubCommand &Subcommand,
224 RefactoringActionCommandLineOptions &Options)
225 : Category(Category), Subcommand(Subcommand), Options(Options) {}
226
227 void visit(const RefactoringOption &Opt, Optional<std::string> &) override {
228 if (Visited.insert(&Opt).second)
229 Options.addStringOption(Opt, create<std::string>(Opt));
230 }
231
232private:
233 template <typename T>
234 std::unique_ptr<cl::opt<T>> create(const RefactoringOption &Opt) {
235 if (!OptionNames.insert(Opt.getName()).second)
236 llvm::report_fatal_error("Multiple identical refactoring options "
237 "specified for one refactoring action");
238 // FIXME: cl::Required can be specified when this option is present
239 // in all rules in an action.
240 return llvm::make_unique<cl::opt<T>>(
241 Opt.getName(), cl::desc(Opt.getDescription()), cl::Optional,
242 cl::cat(Category), cl::sub(Subcommand));
243 }
244
245 llvm::SmallPtrSet<const RefactoringOption *, 8> Visited;
246 llvm::StringSet<> OptionNames;
247 cl::OptionCategory &Category;
248 cl::SubCommand &Subcommand;
249 RefactoringActionCommandLineOptions &Options;
250};
251
Alex Lorenzb54ef6a2017-09-14 10:06:52 +0000252/// A subcommand that corresponds to individual refactoring action.
253class RefactoringActionSubcommand : public cl::SubCommand {
254public:
255 RefactoringActionSubcommand(std::unique_ptr<RefactoringAction> Action,
256 RefactoringActionRules ActionRules,
257 cl::OptionCategory &Category)
258 : SubCommand(Action->getCommand(), Action->getDescription()),
259 Action(std::move(Action)), ActionRules(std::move(ActionRules)) {
Alex Lorenzb54ef6a2017-09-14 10:06:52 +0000260 // Check if the selection option is supported.
261 bool HasSelection = false;
262 for (const auto &Rule : this->ActionRules) {
263 if ((HasSelection = Rule->hasSelectionRequirement()))
264 break;
265 }
266 if (HasSelection) {
267 Selection = llvm::make_unique<cl::opt<std::string>>(
268 "selection",
269 cl::desc("The selected source range in which the refactoring should "
270 "be initiated (<file>:<line>:<column>-<line>:<column> or "
271 "<file>:<line>:<column>)"),
272 cl::cat(Category), cl::sub(*this));
273 }
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
286 /// Parses the command-line arguments that are specific to this rule.
287 ///
288 /// \returns true on error, false otherwise.
289 bool parseArguments() {
290 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:
Alex Lorenzf5ca27c2017-10-16 18:28:26 +0000317 ClangRefactorConsumer() {}
318
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 {
332 SourceChanges.insert(SourceChanges.begin(), Changes.begin(), Changes.end());
333 }
334
335 void handle(SymbolOccurrences Occurrences) override {
Alex Lorenz52843502017-10-16 18:07:16 +0000336 llvm_unreachable("symbol occurrence results are not handled yet");
Alex Lorenze1b7b952017-10-16 17:31:16 +0000337 }
338
339 const AtomicChanges &getSourceChanges() const { return SourceChanges; }
340
341private:
342 AtomicChanges SourceChanges;
Alex Lorenzb54ef6a2017-09-14 10:06:52 +0000343};
344
345class ClangRefactorTool {
346public:
347 std::vector<std::unique_ptr<RefactoringActionSubcommand>> SubCommands;
348
349 ClangRefactorTool() {
350 std::vector<std::unique_ptr<RefactoringAction>> Actions =
351 createRefactoringActions();
352
353 // Actions must have unique command names so that we can map them to one
354 // subcommand.
355 llvm::StringSet<> CommandNames;
356 for (const auto &Action : Actions) {
357 if (!CommandNames.insert(Action->getCommand()).second) {
358 llvm::errs() << "duplicate refactoring action command '"
359 << Action->getCommand() << "'!";
360 exit(1);
361 }
362 }
363
364 // Create subcommands and command-line options.
365 for (auto &Action : Actions) {
366 SubCommands.push_back(llvm::make_unique<RefactoringActionSubcommand>(
367 std::move(Action), Action->createActiveActionRules(),
368 opts::CommonRefactorOptions));
369 }
370 }
371
372 using TUCallbackType = llvm::function_ref<void(ASTContext &)>;
373
374 /// Parses the translation units that were given to the subcommand using
375 /// the 'sources' option and invokes the callback for each parsed
376 /// translation unit.
Alex Lorenz3d712c42017-09-14 13:16:14 +0000377 bool foreachTranslationUnit(const CompilationDatabase &DB,
378 ArrayRef<std::string> Sources,
Alex Lorenzb54ef6a2017-09-14 10:06:52 +0000379 TUCallbackType Callback) {
Alex Lorenzb54ef6a2017-09-14 10:06:52 +0000380 class ToolASTConsumer : public ASTConsumer {
381 public:
382 TUCallbackType Callback;
383 ToolASTConsumer(TUCallbackType Callback) : Callback(Callback) {}
384
385 void HandleTranslationUnit(ASTContext &Context) override {
386 Callback(Context);
387 }
388 };
389 class ActionWrapper {
390 public:
391 TUCallbackType Callback;
392 ActionWrapper(TUCallbackType Callback) : Callback(Callback) {}
393
394 std::unique_ptr<ASTConsumer> newASTConsumer() {
Haojian Wu21cc1382017-10-10 09:48:38 +0000395 return llvm::make_unique<ToolASTConsumer>(Callback);
Alex Lorenzb54ef6a2017-09-14 10:06:52 +0000396 }
397 };
398
Alex Lorenz3d712c42017-09-14 13:16:14 +0000399 ClangTool Tool(DB, Sources);
Haojian Wu21cc1382017-10-10 09:48:38 +0000400 ActionWrapper ToolAction(Callback);
Alex Lorenzb54ef6a2017-09-14 10:06:52 +0000401 std::unique_ptr<tooling::FrontendActionFactory> Factory =
402 tooling::newFrontendActionFactory(&ToolAction);
403 return Tool.run(Factory.get());
404 }
405
406 /// Logs an individual refactoring action invocation to STDOUT.
407 void logInvocation(RefactoringActionSubcommand &Subcommand,
408 const RefactoringRuleContext &Context) {
409 if (!opts::Verbose)
410 return;
411 llvm::outs() << "invoking action '" << Subcommand.getName() << "':\n";
412 if (Context.getSelectionRange().isValid()) {
413 SourceRange R = Context.getSelectionRange();
414 llvm::outs() << " -selection=";
415 R.getBegin().print(llvm::outs(), Context.getSources());
416 llvm::outs() << " -> ";
417 R.getEnd().print(llvm::outs(), Context.getSources());
418 llvm::outs() << "\n";
419 }
420 }
421
Alex Lorenze1b7b952017-10-16 17:31:16 +0000422 bool applySourceChanges(const AtomicChanges &Replacements) {
423 std::set<std::string> Files;
424 for (const auto &Change : Replacements)
425 Files.insert(Change.getFilePath());
426 // FIXME: Add automatic formatting support as well.
427 tooling::ApplyChangesSpec Spec;
428 // FIXME: We should probably cleanup the result by default as well.
429 Spec.Cleanup = false;
430 for (const auto &File : Files) {
431 llvm::ErrorOr<std::unique_ptr<llvm::MemoryBuffer>> BufferErr =
432 llvm::MemoryBuffer::getFile(File);
433 if (!BufferErr) {
434 llvm::errs() << "error: failed to open " << File << " for rewriting\n";
435 return true;
436 }
437 auto Result = tooling::applyAtomicChanges(File, (*BufferErr)->getBuffer(),
438 Replacements, Spec);
439 if (!Result) {
440 llvm::errs() << toString(Result.takeError());
441 return true;
442 }
443
Haojian Wu55186782017-10-20 12:37:16 +0000444 if (opts::Inplace) {
445 std::error_code EC;
446 llvm::raw_fd_ostream OS(File, EC, llvm::sys::fs::F_Text);
447 if (EC) {
448 llvm::errs() << EC.message() << "\n";
449 return true;
450 }
451 OS << *Result;
452 continue;
Alex Lorenze1b7b952017-10-16 17:31:16 +0000453 }
Haojian Wu55186782017-10-20 12:37:16 +0000454
455 llvm::outs() << *Result;
Alex Lorenze1b7b952017-10-16 17:31:16 +0000456 }
457 return false;
458 }
459
Alex Lorenz3d712c42017-09-14 13:16:14 +0000460 bool invokeAction(RefactoringActionSubcommand &Subcommand,
461 const CompilationDatabase &DB,
462 ArrayRef<std::string> Sources) {
Alex Lorenzb54ef6a2017-09-14 10:06:52 +0000463 // Find a set of matching rules.
464 SmallVector<RefactoringActionRule *, 4> MatchingRules;
465 llvm::StringSet<> MissingOptions;
466
467 bool HasSelection = false;
468 for (const auto &Rule : Subcommand.getActionRules()) {
Alex Lorenzad38fbf2017-10-13 01:53:13 +0000469 bool SelectionMatches = true;
Alex Lorenzb54ef6a2017-09-14 10:06:52 +0000470 if (Rule->hasSelectionRequirement()) {
471 HasSelection = true;
Alex Lorenzad38fbf2017-10-13 01:53:13 +0000472 if (!Subcommand.getSelection()) {
Alex Lorenzb54ef6a2017-09-14 10:06:52 +0000473 MissingOptions.insert("selection");
Alex Lorenzad38fbf2017-10-13 01:53:13 +0000474 SelectionMatches = false;
475 }
Alex Lorenzb54ef6a2017-09-14 10:06:52 +0000476 }
Alex Lorenzad38fbf2017-10-13 01:53:13 +0000477 CommandLineRefactoringOptionVisitor Visitor(Subcommand.getOptions());
478 Rule->visitRefactoringOptions(Visitor);
479 if (SelectionMatches && Visitor.getMissingRequiredOptions().empty()) {
480 MatchingRules.push_back(Rule.get());
481 continue;
482 }
483 for (const RefactoringOption *Opt : Visitor.getMissingRequiredOptions())
484 MissingOptions.insert(Opt->getName());
Alex Lorenzb54ef6a2017-09-14 10:06:52 +0000485 }
486 if (MatchingRules.empty()) {
487 llvm::errs() << "error: '" << Subcommand.getName()
488 << "' can't be invoked with the given arguments:\n";
489 for (const auto &Opt : MissingOptions)
490 llvm::errs() << " missing '-" << Opt.getKey() << "' option\n";
491 return true;
492 }
493
Alex Lorenzb54ef6a2017-09-14 10:06:52 +0000494 ClangRefactorConsumer Consumer;
Alex Lorenzf5ca27c2017-10-16 18:28:26 +0000495 bool HasFailed = false;
Alex Lorenz3d712c42017-09-14 13:16:14 +0000496 if (foreachTranslationUnit(DB, Sources, [&](ASTContext &AST) {
Alex Lorenzb54ef6a2017-09-14 10:06:52 +0000497 RefactoringRuleContext Context(AST.getSourceManager());
498 Context.setASTContext(AST);
499
500 auto InvokeRule = [&](RefactoringResultConsumer &Consumer) {
501 logInvocation(Subcommand, Context);
502 for (RefactoringActionRule *Rule : MatchingRules) {
503 if (!Rule->hasSelectionRequirement())
504 continue;
505 Rule->invoke(Consumer, Context);
506 return;
507 }
508 // FIXME (Alex L): If more than one initiation succeeded, then the
509 // rules are ambiguous.
510 llvm_unreachable(
511 "The action must have at least one selection rule");
512 };
513
Alex Lorenzf5ca27c2017-10-16 18:28:26 +0000514 std::unique_ptr<ClangRefactorToolConsumerInterface> CustomConsumer;
515 if (HasSelection)
516 CustomConsumer = Subcommand.getSelection()->createCustomConsumer();
517 ClangRefactorToolConsumerInterface &ActiveConsumer =
518 CustomConsumer ? *CustomConsumer : Consumer;
519 ActiveConsumer.beginTU(AST);
Alex Lorenzb54ef6a2017-09-14 10:06:52 +0000520 if (HasSelection) {
521 assert(Subcommand.getSelection() && "Missing selection argument?");
522 if (opts::Verbose)
523 Subcommand.getSelection()->print(llvm::outs());
Alex Lorenzb54ef6a2017-09-14 10:06:52 +0000524 if (Subcommand.getSelection()->forAllRanges(
525 Context.getSources(), [&](SourceRange R) {
526 Context.setSelectionRange(R);
Alex Lorenzf5ca27c2017-10-16 18:28:26 +0000527 InvokeRule(ActiveConsumer);
Alex Lorenzb54ef6a2017-09-14 10:06:52 +0000528 }))
529 HasFailed = true;
Alex Lorenzf5ca27c2017-10-16 18:28:26 +0000530 ActiveConsumer.endTU();
Alex Lorenzb54ef6a2017-09-14 10:06:52 +0000531 return;
532 }
533 // FIXME (Alex L): Implement non-selection based invocation path.
Alex Lorenzf5ca27c2017-10-16 18:28:26 +0000534 ActiveConsumer.endTU();
Alex Lorenzb54ef6a2017-09-14 10:06:52 +0000535 }))
536 return true;
Alex Lorenze1b7b952017-10-16 17:31:16 +0000537 return HasFailed || applySourceChanges(Consumer.getSourceChanges());
Alex Lorenzb54ef6a2017-09-14 10:06:52 +0000538 }
539};
540
541} // end anonymous namespace
542
543int main(int argc, const char **argv) {
544 ClangRefactorTool Tool;
545
Alex Lorenz3d712c42017-09-14 13:16:14 +0000546 CommonOptionsParser Options(
Alex Lorenzad38fbf2017-10-13 01:53:13 +0000547 argc, argv, cl::GeneralCategory, cl::ZeroOrMore,
Alex Lorenz3d712c42017-09-14 13:16:14 +0000548 "Clang-based refactoring tool for C, C++ and Objective-C");
Alex Lorenzb54ef6a2017-09-14 10:06:52 +0000549
550 // Figure out which action is specified by the user. The user must specify
551 // the action using a command-line subcommand, e.g. the invocation
552 // `clang-refactor local-rename` corresponds to the `LocalRename` refactoring
553 // action. All subcommands must have a unique names. This allows us to figure
554 // out which refactoring action should be invoked by looking at the first
555 // subcommand that's enabled by LLVM's command-line parser.
556 auto It = llvm::find_if(
557 Tool.SubCommands,
558 [](const std::unique_ptr<RefactoringActionSubcommand> &SubCommand) {
559 return !!(*SubCommand);
560 });
561 if (It == Tool.SubCommands.end()) {
562 llvm::errs() << "error: no refactoring action given\n";
563 llvm::errs() << "note: the following actions are supported:\n";
564 for (const auto &Subcommand : Tool.SubCommands)
565 llvm::errs().indent(2) << Subcommand->getName() << "\n";
566 return 1;
567 }
568 RefactoringActionSubcommand &ActionCommand = **It;
569
Alex Lorenzb54ef6a2017-09-14 10:06:52 +0000570 if (ActionCommand.parseArguments())
571 return 1;
Alex Lorenz3d712c42017-09-14 13:16:14 +0000572 if (Tool.invokeAction(ActionCommand, Options.getCompilations(),
573 Options.getSourcePathList()))
Alex Lorenzb54ef6a2017-09-14 10:06:52 +0000574 return 1;
575
576 return 0;
577}