blob: ed9bf5ed28d4df3c560c86f5d6ccad62824aa9a8 [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));
43} // end namespace opts
44
45namespace {
46
47/// Stores the parsed `-selection` argument.
48class SourceSelectionArgument {
49public:
50 virtual ~SourceSelectionArgument() {}
51
52 /// Parse the `-selection` argument.
53 ///
54 /// \returns A valid argument when the parse succedeed, null otherwise.
55 static std::unique_ptr<SourceSelectionArgument> fromString(StringRef Value);
56
57 /// Prints any additional state associated with the selection argument to
58 /// the given output stream.
Alex Lorenze1b7b952017-10-16 17:31:16 +000059 virtual void print(raw_ostream &OS) {}
Alex Lorenzb54ef6a2017-09-14 10:06:52 +000060
61 /// Returns a replacement refactoring result consumer (if any) that should
62 /// consume the results of a refactoring operation.
63 ///
64 /// The replacement refactoring result consumer is used by \c
65 /// TestSourceSelectionArgument to inject a test-specific result handling
66 /// logic into the refactoring operation. The test-specific consumer
67 /// ensures that the individual results in a particular test group are
68 /// identical.
Alex Lorenzf5ca27c2017-10-16 18:28:26 +000069 virtual std::unique_ptr<ClangRefactorToolConsumerInterface>
70 createCustomConsumer() {
Alex Lorenzb54ef6a2017-09-14 10:06:52 +000071 return nullptr;
72 }
73
74 /// Runs the give refactoring function for each specified selection.
75 ///
76 /// \returns true if an error occurred, false otherwise.
77 virtual bool
78 forAllRanges(const SourceManager &SM,
79 llvm::function_ref<void(SourceRange R)> Callback) = 0;
80};
81
82/// Stores the parsed -selection=test:<filename> option.
83class TestSourceSelectionArgument final : public SourceSelectionArgument {
84public:
85 TestSourceSelectionArgument(TestSelectionRangesInFile TestSelections)
86 : TestSelections(std::move(TestSelections)) {}
87
88 void print(raw_ostream &OS) override { TestSelections.dump(OS); }
89
Alex Lorenzf5ca27c2017-10-16 18:28:26 +000090 std::unique_ptr<ClangRefactorToolConsumerInterface>
91 createCustomConsumer() override {
Alex Lorenzb54ef6a2017-09-14 10:06:52 +000092 return TestSelections.createConsumer();
93 }
94
95 /// Testing support: invokes the selection action for each selection range in
96 /// the test file.
97 bool forAllRanges(const SourceManager &SM,
98 llvm::function_ref<void(SourceRange R)> Callback) override {
99 return TestSelections.foreachRange(SM, Callback);
100 }
101
102private:
103 TestSelectionRangesInFile TestSelections;
104};
105
Alex Lorenze1b7b952017-10-16 17:31:16 +0000106/// Stores the parsed -selection=filename:line:column[-line:column] option.
107class SourceRangeSelectionArgument final : public SourceSelectionArgument {
108public:
109 SourceRangeSelectionArgument(ParsedSourceRange Range)
110 : Range(std::move(Range)) {}
111
112 bool forAllRanges(const SourceManager &SM,
113 llvm::function_ref<void(SourceRange R)> Callback) override {
114 const FileEntry *FE = SM.getFileManager().getFile(Range.FileName);
115 FileID FID = FE ? SM.translateFile(FE) : FileID();
116 if (!FE || FID.isInvalid()) {
117 llvm::errs() << "error: -selection=" << Range.FileName
118 << ":... : given file is not in the target TU\n";
119 return true;
120 }
121
122 SourceLocation Start = SM.getMacroArgExpandedLocation(
123 SM.translateLineCol(FID, Range.Begin.first, Range.Begin.second));
124 SourceLocation End = SM.getMacroArgExpandedLocation(
125 SM.translateLineCol(FID, Range.End.first, Range.End.second));
126 if (Start.isInvalid() || End.isInvalid()) {
127 llvm::errs() << "error: -selection=" << Range.FileName << ':'
128 << Range.Begin.first << ':' << Range.Begin.second << '-'
129 << Range.End.first << ':' << Range.End.second
130 << " : invalid source location\n";
131 return true;
132 }
133 Callback(SourceRange(Start, End));
134 return false;
135 }
136
137private:
138 ParsedSourceRange Range;
139};
140
Alex Lorenzb54ef6a2017-09-14 10:06:52 +0000141std::unique_ptr<SourceSelectionArgument>
142SourceSelectionArgument::fromString(StringRef Value) {
143 if (Value.startswith("test:")) {
144 StringRef Filename = Value.drop_front(strlen("test:"));
145 Optional<TestSelectionRangesInFile> ParsedTestSelection =
146 findTestSelectionRanges(Filename);
147 if (!ParsedTestSelection)
148 return nullptr; // A parsing error was already reported.
149 return llvm::make_unique<TestSourceSelectionArgument>(
150 std::move(*ParsedTestSelection));
151 }
Alex Lorenze1b7b952017-10-16 17:31:16 +0000152 Optional<ParsedSourceRange> Range = ParsedSourceRange::fromString(Value);
153 if (Range)
154 return llvm::make_unique<SourceRangeSelectionArgument>(std::move(*Range));
Alex Lorenzb54ef6a2017-09-14 10:06:52 +0000155 llvm::errs() << "error: '-selection' option must be specified using "
156 "<file>:<line>:<column> or "
Alex Lorenze1b7b952017-10-16 17:31:16 +0000157 "<file>:<line>:<column>-<line>:<column> format\n";
Alex Lorenzb54ef6a2017-09-14 10:06:52 +0000158 return nullptr;
159}
160
Alex Lorenzad38fbf2017-10-13 01:53:13 +0000161/// A container that stores the command-line options used by a single
162/// refactoring option.
163class RefactoringActionCommandLineOptions {
164public:
165 void addStringOption(const RefactoringOption &Option,
166 std::unique_ptr<cl::opt<std::string>> CLOption) {
167 StringOptions[&Option] = std::move(CLOption);
168 }
169
170 const cl::opt<std::string> &
171 getStringOption(const RefactoringOption &Opt) const {
172 auto It = StringOptions.find(&Opt);
173 return *It->second;
174 }
175
176private:
177 llvm::DenseMap<const RefactoringOption *,
178 std::unique_ptr<cl::opt<std::string>>>
179 StringOptions;
180};
181
182/// Passes the command-line option values to the options used by a single
183/// refactoring action rule.
184class CommandLineRefactoringOptionVisitor final
185 : public RefactoringOptionVisitor {
186public:
187 CommandLineRefactoringOptionVisitor(
188 const RefactoringActionCommandLineOptions &Options)
189 : Options(Options) {}
190
191 void visit(const RefactoringOption &Opt,
192 Optional<std::string> &Value) override {
193 const cl::opt<std::string> &CLOpt = Options.getStringOption(Opt);
194 if (!CLOpt.getValue().empty()) {
195 Value = CLOpt.getValue();
196 return;
197 }
198 Value = None;
199 if (Opt.isRequired())
200 MissingRequiredOptions.push_back(&Opt);
201 }
202
203 ArrayRef<const RefactoringOption *> getMissingRequiredOptions() const {
204 return MissingRequiredOptions;
205 }
206
207private:
208 llvm::SmallVector<const RefactoringOption *, 4> MissingRequiredOptions;
209 const RefactoringActionCommandLineOptions &Options;
210};
211
212/// Creates the refactoring options used by all the rules in a single
213/// refactoring action.
214class CommandLineRefactoringOptionCreator final
215 : public RefactoringOptionVisitor {
216public:
217 CommandLineRefactoringOptionCreator(
218 cl::OptionCategory &Category, cl::SubCommand &Subcommand,
219 RefactoringActionCommandLineOptions &Options)
220 : Category(Category), Subcommand(Subcommand), Options(Options) {}
221
222 void visit(const RefactoringOption &Opt, Optional<std::string> &) override {
223 if (Visited.insert(&Opt).second)
224 Options.addStringOption(Opt, create<std::string>(Opt));
225 }
226
227private:
228 template <typename T>
229 std::unique_ptr<cl::opt<T>> create(const RefactoringOption &Opt) {
230 if (!OptionNames.insert(Opt.getName()).second)
231 llvm::report_fatal_error("Multiple identical refactoring options "
232 "specified for one refactoring action");
233 // FIXME: cl::Required can be specified when this option is present
234 // in all rules in an action.
235 return llvm::make_unique<cl::opt<T>>(
236 Opt.getName(), cl::desc(Opt.getDescription()), cl::Optional,
237 cl::cat(Category), cl::sub(Subcommand));
238 }
239
240 llvm::SmallPtrSet<const RefactoringOption *, 8> Visited;
241 llvm::StringSet<> OptionNames;
242 cl::OptionCategory &Category;
243 cl::SubCommand &Subcommand;
244 RefactoringActionCommandLineOptions &Options;
245};
246
Alex Lorenzb54ef6a2017-09-14 10:06:52 +0000247/// A subcommand that corresponds to individual refactoring action.
248class RefactoringActionSubcommand : public cl::SubCommand {
249public:
250 RefactoringActionSubcommand(std::unique_ptr<RefactoringAction> Action,
251 RefactoringActionRules ActionRules,
252 cl::OptionCategory &Category)
253 : SubCommand(Action->getCommand(), Action->getDescription()),
254 Action(std::move(Action)), ActionRules(std::move(ActionRules)) {
Alex Lorenzb54ef6a2017-09-14 10:06:52 +0000255 // Check if the selection option is supported.
256 bool HasSelection = false;
257 for (const auto &Rule : this->ActionRules) {
258 if ((HasSelection = Rule->hasSelectionRequirement()))
259 break;
260 }
261 if (HasSelection) {
262 Selection = llvm::make_unique<cl::opt<std::string>>(
263 "selection",
264 cl::desc("The selected source range in which the refactoring should "
265 "be initiated (<file>:<line>:<column>-<line>:<column> or "
266 "<file>:<line>:<column>)"),
267 cl::cat(Category), cl::sub(*this));
268 }
Alex Lorenzad38fbf2017-10-13 01:53:13 +0000269 // Create the refactoring options.
270 for (const auto &Rule : this->ActionRules) {
271 CommandLineRefactoringOptionCreator OptionCreator(Category, *this,
272 Options);
273 Rule->visitRefactoringOptions(OptionCreator);
274 }
Alex Lorenzb54ef6a2017-09-14 10:06:52 +0000275 }
276
277 ~RefactoringActionSubcommand() { unregisterSubCommand(); }
278
279 const RefactoringActionRules &getActionRules() const { return ActionRules; }
280
281 /// Parses the command-line arguments that are specific to this rule.
282 ///
283 /// \returns true on error, false otherwise.
284 bool parseArguments() {
285 if (Selection) {
286 ParsedSelection = SourceSelectionArgument::fromString(*Selection);
287 if (!ParsedSelection)
288 return true;
289 }
290 return false;
291 }
292
293 SourceSelectionArgument *getSelection() const {
294 assert(Selection && "selection not supported!");
295 return ParsedSelection.get();
296 }
Alex Lorenzad38fbf2017-10-13 01:53:13 +0000297
298 const RefactoringActionCommandLineOptions &getOptions() const {
299 return Options;
300 }
301
Alex Lorenzb54ef6a2017-09-14 10:06:52 +0000302private:
303 std::unique_ptr<RefactoringAction> Action;
304 RefactoringActionRules ActionRules;
Alex Lorenzb54ef6a2017-09-14 10:06:52 +0000305 std::unique_ptr<cl::opt<std::string>> Selection;
306 std::unique_ptr<SourceSelectionArgument> ParsedSelection;
Alex Lorenzad38fbf2017-10-13 01:53:13 +0000307 RefactoringActionCommandLineOptions Options;
Alex Lorenzb54ef6a2017-09-14 10:06:52 +0000308};
309
Alex Lorenzf5ca27c2017-10-16 18:28:26 +0000310class ClangRefactorConsumer final : public ClangRefactorToolConsumerInterface {
Alex Lorenzb54ef6a2017-09-14 10:06:52 +0000311public:
Alex Lorenzf5ca27c2017-10-16 18:28:26 +0000312 ClangRefactorConsumer() {}
313
Alex Lorenze1b7b952017-10-16 17:31:16 +0000314 void handleError(llvm::Error Err) override {
Alex Lorenzf5ca27c2017-10-16 18:28:26 +0000315 Optional<PartialDiagnosticAt> Diag = DiagnosticError::take(Err);
316 if (!Diag) {
317 llvm::errs() << llvm::toString(std::move(Err)) << "\n";
318 return;
319 }
320 llvm::cantFail(std::move(Err)); // This is a success.
321 DiagnosticBuilder DB(
322 getDiags().Report(Diag->first, Diag->second.getDiagID()));
323 Diag->second.Emit(DB);
Alex Lorenzb54ef6a2017-09-14 10:06:52 +0000324 }
325
Alex Lorenze1b7b952017-10-16 17:31:16 +0000326 void handle(AtomicChanges Changes) override {
327 SourceChanges.insert(SourceChanges.begin(), Changes.begin(), Changes.end());
328 }
329
330 void handle(SymbolOccurrences Occurrences) override {
Alex Lorenz52843502017-10-16 18:07:16 +0000331 llvm_unreachable("symbol occurrence results are not handled yet");
Alex Lorenze1b7b952017-10-16 17:31:16 +0000332 }
333
334 const AtomicChanges &getSourceChanges() const { return SourceChanges; }
335
336private:
337 AtomicChanges SourceChanges;
Alex Lorenzb54ef6a2017-09-14 10:06:52 +0000338};
339
340class ClangRefactorTool {
341public:
342 std::vector<std::unique_ptr<RefactoringActionSubcommand>> SubCommands;
343
344 ClangRefactorTool() {
345 std::vector<std::unique_ptr<RefactoringAction>> Actions =
346 createRefactoringActions();
347
348 // Actions must have unique command names so that we can map them to one
349 // subcommand.
350 llvm::StringSet<> CommandNames;
351 for (const auto &Action : Actions) {
352 if (!CommandNames.insert(Action->getCommand()).second) {
353 llvm::errs() << "duplicate refactoring action command '"
354 << Action->getCommand() << "'!";
355 exit(1);
356 }
357 }
358
359 // Create subcommands and command-line options.
360 for (auto &Action : Actions) {
361 SubCommands.push_back(llvm::make_unique<RefactoringActionSubcommand>(
362 std::move(Action), Action->createActiveActionRules(),
363 opts::CommonRefactorOptions));
364 }
365 }
366
367 using TUCallbackType = llvm::function_ref<void(ASTContext &)>;
368
369 /// Parses the translation units that were given to the subcommand using
370 /// the 'sources' option and invokes the callback for each parsed
371 /// translation unit.
Alex Lorenz3d712c42017-09-14 13:16:14 +0000372 bool foreachTranslationUnit(const CompilationDatabase &DB,
373 ArrayRef<std::string> Sources,
Alex Lorenzb54ef6a2017-09-14 10:06:52 +0000374 TUCallbackType Callback) {
Alex Lorenzb54ef6a2017-09-14 10:06:52 +0000375 class ToolASTConsumer : public ASTConsumer {
376 public:
377 TUCallbackType Callback;
378 ToolASTConsumer(TUCallbackType Callback) : Callback(Callback) {}
379
380 void HandleTranslationUnit(ASTContext &Context) override {
381 Callback(Context);
382 }
383 };
384 class ActionWrapper {
385 public:
386 TUCallbackType Callback;
387 ActionWrapper(TUCallbackType Callback) : Callback(Callback) {}
388
389 std::unique_ptr<ASTConsumer> newASTConsumer() {
Haojian Wu21cc1382017-10-10 09:48:38 +0000390 return llvm::make_unique<ToolASTConsumer>(Callback);
Alex Lorenzb54ef6a2017-09-14 10:06:52 +0000391 }
392 };
393
Alex Lorenz3d712c42017-09-14 13:16:14 +0000394 ClangTool Tool(DB, Sources);
Haojian Wu21cc1382017-10-10 09:48:38 +0000395 ActionWrapper ToolAction(Callback);
Alex Lorenzb54ef6a2017-09-14 10:06:52 +0000396 std::unique_ptr<tooling::FrontendActionFactory> Factory =
397 tooling::newFrontendActionFactory(&ToolAction);
398 return Tool.run(Factory.get());
399 }
400
401 /// Logs an individual refactoring action invocation to STDOUT.
402 void logInvocation(RefactoringActionSubcommand &Subcommand,
403 const RefactoringRuleContext &Context) {
404 if (!opts::Verbose)
405 return;
406 llvm::outs() << "invoking action '" << Subcommand.getName() << "':\n";
407 if (Context.getSelectionRange().isValid()) {
408 SourceRange R = Context.getSelectionRange();
409 llvm::outs() << " -selection=";
410 R.getBegin().print(llvm::outs(), Context.getSources());
411 llvm::outs() << " -> ";
412 R.getEnd().print(llvm::outs(), Context.getSources());
413 llvm::outs() << "\n";
414 }
415 }
416
Alex Lorenze1b7b952017-10-16 17:31:16 +0000417 bool applySourceChanges(const AtomicChanges &Replacements) {
418 std::set<std::string> Files;
419 for (const auto &Change : Replacements)
420 Files.insert(Change.getFilePath());
421 // FIXME: Add automatic formatting support as well.
422 tooling::ApplyChangesSpec Spec;
423 // FIXME: We should probably cleanup the result by default as well.
424 Spec.Cleanup = false;
425 for (const auto &File : Files) {
426 llvm::ErrorOr<std::unique_ptr<llvm::MemoryBuffer>> BufferErr =
427 llvm::MemoryBuffer::getFile(File);
428 if (!BufferErr) {
429 llvm::errs() << "error: failed to open " << File << " for rewriting\n";
430 return true;
431 }
432 auto Result = tooling::applyAtomicChanges(File, (*BufferErr)->getBuffer(),
433 Replacements, Spec);
434 if (!Result) {
435 llvm::errs() << toString(Result.takeError());
436 return true;
437 }
438
439 std::error_code EC;
440 llvm::raw_fd_ostream OS(File, EC, llvm::sys::fs::F_Text);
441 if (EC) {
442 llvm::errs() << EC.message() << "\n";
443 return true;
444 }
445 OS << *Result;
446 }
447 return false;
448 }
449
Alex Lorenz3d712c42017-09-14 13:16:14 +0000450 bool invokeAction(RefactoringActionSubcommand &Subcommand,
451 const CompilationDatabase &DB,
452 ArrayRef<std::string> Sources) {
Alex Lorenzb54ef6a2017-09-14 10:06:52 +0000453 // Find a set of matching rules.
454 SmallVector<RefactoringActionRule *, 4> MatchingRules;
455 llvm::StringSet<> MissingOptions;
456
457 bool HasSelection = false;
458 for (const auto &Rule : Subcommand.getActionRules()) {
Alex Lorenzad38fbf2017-10-13 01:53:13 +0000459 bool SelectionMatches = true;
Alex Lorenzb54ef6a2017-09-14 10:06:52 +0000460 if (Rule->hasSelectionRequirement()) {
461 HasSelection = true;
Alex Lorenzad38fbf2017-10-13 01:53:13 +0000462 if (!Subcommand.getSelection()) {
Alex Lorenzb54ef6a2017-09-14 10:06:52 +0000463 MissingOptions.insert("selection");
Alex Lorenzad38fbf2017-10-13 01:53:13 +0000464 SelectionMatches = false;
465 }
Alex Lorenzb54ef6a2017-09-14 10:06:52 +0000466 }
Alex Lorenzad38fbf2017-10-13 01:53:13 +0000467 CommandLineRefactoringOptionVisitor Visitor(Subcommand.getOptions());
468 Rule->visitRefactoringOptions(Visitor);
469 if (SelectionMatches && Visitor.getMissingRequiredOptions().empty()) {
470 MatchingRules.push_back(Rule.get());
471 continue;
472 }
473 for (const RefactoringOption *Opt : Visitor.getMissingRequiredOptions())
474 MissingOptions.insert(Opt->getName());
Alex Lorenzb54ef6a2017-09-14 10:06:52 +0000475 }
476 if (MatchingRules.empty()) {
477 llvm::errs() << "error: '" << Subcommand.getName()
478 << "' can't be invoked with the given arguments:\n";
479 for (const auto &Opt : MissingOptions)
480 llvm::errs() << " missing '-" << Opt.getKey() << "' option\n";
481 return true;
482 }
483
Alex Lorenzb54ef6a2017-09-14 10:06:52 +0000484 ClangRefactorConsumer Consumer;
Alex Lorenzf5ca27c2017-10-16 18:28:26 +0000485 bool HasFailed = false;
Alex Lorenz3d712c42017-09-14 13:16:14 +0000486 if (foreachTranslationUnit(DB, Sources, [&](ASTContext &AST) {
Alex Lorenzb54ef6a2017-09-14 10:06:52 +0000487 RefactoringRuleContext Context(AST.getSourceManager());
488 Context.setASTContext(AST);
489
490 auto InvokeRule = [&](RefactoringResultConsumer &Consumer) {
491 logInvocation(Subcommand, Context);
492 for (RefactoringActionRule *Rule : MatchingRules) {
493 if (!Rule->hasSelectionRequirement())
494 continue;
495 Rule->invoke(Consumer, Context);
496 return;
497 }
498 // FIXME (Alex L): If more than one initiation succeeded, then the
499 // rules are ambiguous.
500 llvm_unreachable(
501 "The action must have at least one selection rule");
502 };
503
Alex Lorenzf5ca27c2017-10-16 18:28:26 +0000504 std::unique_ptr<ClangRefactorToolConsumerInterface> CustomConsumer;
505 if (HasSelection)
506 CustomConsumer = Subcommand.getSelection()->createCustomConsumer();
507 ClangRefactorToolConsumerInterface &ActiveConsumer =
508 CustomConsumer ? *CustomConsumer : Consumer;
509 ActiveConsumer.beginTU(AST);
Alex Lorenzb54ef6a2017-09-14 10:06:52 +0000510 if (HasSelection) {
511 assert(Subcommand.getSelection() && "Missing selection argument?");
512 if (opts::Verbose)
513 Subcommand.getSelection()->print(llvm::outs());
Alex Lorenzb54ef6a2017-09-14 10:06:52 +0000514 if (Subcommand.getSelection()->forAllRanges(
515 Context.getSources(), [&](SourceRange R) {
516 Context.setSelectionRange(R);
Alex Lorenzf5ca27c2017-10-16 18:28:26 +0000517 InvokeRule(ActiveConsumer);
Alex Lorenzb54ef6a2017-09-14 10:06:52 +0000518 }))
519 HasFailed = true;
Alex Lorenzf5ca27c2017-10-16 18:28:26 +0000520 ActiveConsumer.endTU();
Alex Lorenzb54ef6a2017-09-14 10:06:52 +0000521 return;
522 }
523 // FIXME (Alex L): Implement non-selection based invocation path.
Alex Lorenzf5ca27c2017-10-16 18:28:26 +0000524 ActiveConsumer.endTU();
Alex Lorenzb54ef6a2017-09-14 10:06:52 +0000525 }))
526 return true;
Alex Lorenze1b7b952017-10-16 17:31:16 +0000527 return HasFailed || applySourceChanges(Consumer.getSourceChanges());
Alex Lorenzb54ef6a2017-09-14 10:06:52 +0000528 }
529};
530
531} // end anonymous namespace
532
533int main(int argc, const char **argv) {
534 ClangRefactorTool Tool;
535
Alex Lorenz3d712c42017-09-14 13:16:14 +0000536 CommonOptionsParser Options(
Alex Lorenzad38fbf2017-10-13 01:53:13 +0000537 argc, argv, cl::GeneralCategory, cl::ZeroOrMore,
Alex Lorenz3d712c42017-09-14 13:16:14 +0000538 "Clang-based refactoring tool for C, C++ and Objective-C");
Alex Lorenzb54ef6a2017-09-14 10:06:52 +0000539
540 // Figure out which action is specified by the user. The user must specify
541 // the action using a command-line subcommand, e.g. the invocation
542 // `clang-refactor local-rename` corresponds to the `LocalRename` refactoring
543 // action. All subcommands must have a unique names. This allows us to figure
544 // out which refactoring action should be invoked by looking at the first
545 // subcommand that's enabled by LLVM's command-line parser.
546 auto It = llvm::find_if(
547 Tool.SubCommands,
548 [](const std::unique_ptr<RefactoringActionSubcommand> &SubCommand) {
549 return !!(*SubCommand);
550 });
551 if (It == Tool.SubCommands.end()) {
552 llvm::errs() << "error: no refactoring action given\n";
553 llvm::errs() << "note: the following actions are supported:\n";
554 for (const auto &Subcommand : Tool.SubCommands)
555 llvm::errs().indent(2) << Subcommand->getName() << "\n";
556 return 1;
557 }
558 RefactoringActionSubcommand &ActionCommand = **It;
559
Alex Lorenzb54ef6a2017-09-14 10:06:52 +0000560 if (ActionCommand.parseArguments())
561 return 1;
Alex Lorenz3d712c42017-09-14 13:16:14 +0000562 if (Tool.invokeAction(ActionCommand, Options.getCompilations(),
563 Options.getSourcePathList()))
Alex Lorenzb54ef6a2017-09-14 10:06:52 +0000564 return 1;
565
566 return 0;
567}