xref: /openbsd-src/gnu/llvm/clang/tools/clang-refactor/ClangRefactor.cpp (revision 12c855180aad702bbcca06e0398d774beeafb155)
1e5dd7070Spatrick //===--- ClangRefactor.cpp - Clang-based refactoring tool -----------------===//
2e5dd7070Spatrick //
3e5dd7070Spatrick // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4e5dd7070Spatrick // See https://llvm.org/LICENSE.txt for license information.
5e5dd7070Spatrick // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6e5dd7070Spatrick //
7e5dd7070Spatrick //===----------------------------------------------------------------------===//
8e5dd7070Spatrick ///
9e5dd7070Spatrick /// \file
10e5dd7070Spatrick /// This file implements a clang-refactor tool that performs various
11e5dd7070Spatrick /// source transformations.
12e5dd7070Spatrick ///
13e5dd7070Spatrick //===----------------------------------------------------------------------===//
14e5dd7070Spatrick 
15e5dd7070Spatrick #include "TestSupport.h"
16e5dd7070Spatrick #include "clang/Frontend/CommandLineSourceLoc.h"
17e5dd7070Spatrick #include "clang/Frontend/TextDiagnosticPrinter.h"
18e5dd7070Spatrick #include "clang/Rewrite/Core/Rewriter.h"
19e5dd7070Spatrick #include "clang/Tooling/CommonOptionsParser.h"
20e5dd7070Spatrick #include "clang/Tooling/Refactoring.h"
21e5dd7070Spatrick #include "clang/Tooling/Refactoring/RefactoringAction.h"
22e5dd7070Spatrick #include "clang/Tooling/Refactoring/RefactoringOptions.h"
23e5dd7070Spatrick #include "clang/Tooling/Refactoring/Rename/RenamingAction.h"
24e5dd7070Spatrick #include "clang/Tooling/Tooling.h"
25e5dd7070Spatrick #include "llvm/Support/CommandLine.h"
26e5dd7070Spatrick #include "llvm/Support/FileSystem.h"
27e5dd7070Spatrick #include "llvm/Support/Signals.h"
28e5dd7070Spatrick #include "llvm/Support/raw_ostream.h"
29*12c85518Srobert #include <optional>
30e5dd7070Spatrick #include <string>
31e5dd7070Spatrick 
32e5dd7070Spatrick using namespace clang;
33e5dd7070Spatrick using namespace tooling;
34e5dd7070Spatrick using namespace refactor;
35e5dd7070Spatrick namespace cl = llvm::cl;
36e5dd7070Spatrick 
37e5dd7070Spatrick namespace opts {
38e5dd7070Spatrick 
39e5dd7070Spatrick static cl::OptionCategory CommonRefactorOptions("Refactoring options");
40e5dd7070Spatrick 
41e5dd7070Spatrick static cl::opt<bool> Verbose("v", cl::desc("Use verbose output"),
42a9ac8606Spatrick                              cl::cat(cl::getGeneralCategory()),
43*12c85518Srobert                              cl::sub(cl::SubCommand::getAll()));
44e5dd7070Spatrick 
45e5dd7070Spatrick static cl::opt<bool> Inplace("i", cl::desc("Inplace edit <file>s"),
46a9ac8606Spatrick                              cl::cat(cl::getGeneralCategory()),
47*12c85518Srobert                              cl::sub(cl::SubCommand::getAll()));
48e5dd7070Spatrick 
49e5dd7070Spatrick } // end namespace opts
50e5dd7070Spatrick 
51e5dd7070Spatrick namespace {
52e5dd7070Spatrick 
53e5dd7070Spatrick /// Stores the parsed `-selection` argument.
54e5dd7070Spatrick class SourceSelectionArgument {
55e5dd7070Spatrick public:
~SourceSelectionArgument()56e5dd7070Spatrick   virtual ~SourceSelectionArgument() {}
57e5dd7070Spatrick 
58e5dd7070Spatrick   /// Parse the `-selection` argument.
59e5dd7070Spatrick   ///
60e5dd7070Spatrick   /// \returns A valid argument when the parse succedeed, null otherwise.
61e5dd7070Spatrick   static std::unique_ptr<SourceSelectionArgument> fromString(StringRef Value);
62e5dd7070Spatrick 
63e5dd7070Spatrick   /// Prints any additional state associated with the selection argument to
64e5dd7070Spatrick   /// the given output stream.
print(raw_ostream & OS)65e5dd7070Spatrick   virtual void print(raw_ostream &OS) {}
66e5dd7070Spatrick 
67e5dd7070Spatrick   /// Returns a replacement refactoring result consumer (if any) that should
68e5dd7070Spatrick   /// consume the results of a refactoring operation.
69e5dd7070Spatrick   ///
70e5dd7070Spatrick   /// The replacement refactoring result consumer is used by \c
71e5dd7070Spatrick   /// TestSourceSelectionArgument to inject a test-specific result handling
72e5dd7070Spatrick   /// logic into the refactoring operation. The test-specific consumer
73e5dd7070Spatrick   /// ensures that the individual results in a particular test group are
74e5dd7070Spatrick   /// identical.
75e5dd7070Spatrick   virtual std::unique_ptr<ClangRefactorToolConsumerInterface>
createCustomConsumer()76e5dd7070Spatrick   createCustomConsumer() {
77e5dd7070Spatrick     return nullptr;
78e5dd7070Spatrick   }
79e5dd7070Spatrick 
80e5dd7070Spatrick   /// Runs the give refactoring function for each specified selection.
81e5dd7070Spatrick   ///
82e5dd7070Spatrick   /// \returns true if an error occurred, false otherwise.
83e5dd7070Spatrick   virtual bool
84e5dd7070Spatrick   forAllRanges(const SourceManager &SM,
85e5dd7070Spatrick                llvm::function_ref<void(SourceRange R)> Callback) = 0;
86e5dd7070Spatrick };
87e5dd7070Spatrick 
88e5dd7070Spatrick /// Stores the parsed -selection=test:<filename> option.
89e5dd7070Spatrick class TestSourceSelectionArgument final : public SourceSelectionArgument {
90e5dd7070Spatrick public:
TestSourceSelectionArgument(TestSelectionRangesInFile TestSelections)91e5dd7070Spatrick   TestSourceSelectionArgument(TestSelectionRangesInFile TestSelections)
92e5dd7070Spatrick       : TestSelections(std::move(TestSelections)) {}
93e5dd7070Spatrick 
print(raw_ostream & OS)94e5dd7070Spatrick   void print(raw_ostream &OS) override { TestSelections.dump(OS); }
95e5dd7070Spatrick 
96e5dd7070Spatrick   std::unique_ptr<ClangRefactorToolConsumerInterface>
createCustomConsumer()97e5dd7070Spatrick   createCustomConsumer() override {
98e5dd7070Spatrick     return TestSelections.createConsumer();
99e5dd7070Spatrick   }
100e5dd7070Spatrick 
101e5dd7070Spatrick   /// Testing support: invokes the selection action for each selection range in
102e5dd7070Spatrick   /// the test file.
forAllRanges(const SourceManager & SM,llvm::function_ref<void (SourceRange R)> Callback)103e5dd7070Spatrick   bool forAllRanges(const SourceManager &SM,
104e5dd7070Spatrick                     llvm::function_ref<void(SourceRange R)> Callback) override {
105e5dd7070Spatrick     return TestSelections.foreachRange(SM, Callback);
106e5dd7070Spatrick   }
107e5dd7070Spatrick 
108e5dd7070Spatrick private:
109e5dd7070Spatrick   TestSelectionRangesInFile TestSelections;
110e5dd7070Spatrick };
111e5dd7070Spatrick 
112e5dd7070Spatrick /// Stores the parsed -selection=filename:line:column[-line:column] option.
113e5dd7070Spatrick class SourceRangeSelectionArgument final : public SourceSelectionArgument {
114e5dd7070Spatrick public:
SourceRangeSelectionArgument(ParsedSourceRange Range)115e5dd7070Spatrick   SourceRangeSelectionArgument(ParsedSourceRange Range)
116e5dd7070Spatrick       : Range(std::move(Range)) {}
117e5dd7070Spatrick 
forAllRanges(const SourceManager & SM,llvm::function_ref<void (SourceRange R)> Callback)118e5dd7070Spatrick   bool forAllRanges(const SourceManager &SM,
119e5dd7070Spatrick                     llvm::function_ref<void(SourceRange R)> Callback) override {
120e5dd7070Spatrick     auto FE = SM.getFileManager().getFile(Range.FileName);
121e5dd7070Spatrick     FileID FID = FE ? SM.translateFile(*FE) : FileID();
122e5dd7070Spatrick     if (!FE || FID.isInvalid()) {
123e5dd7070Spatrick       llvm::errs() << "error: -selection=" << Range.FileName
124e5dd7070Spatrick                    << ":... : given file is not in the target TU\n";
125e5dd7070Spatrick       return true;
126e5dd7070Spatrick     }
127e5dd7070Spatrick 
128e5dd7070Spatrick     SourceLocation Start = SM.getMacroArgExpandedLocation(
129e5dd7070Spatrick         SM.translateLineCol(FID, Range.Begin.first, Range.Begin.second));
130e5dd7070Spatrick     SourceLocation End = SM.getMacroArgExpandedLocation(
131e5dd7070Spatrick         SM.translateLineCol(FID, Range.End.first, Range.End.second));
132e5dd7070Spatrick     if (Start.isInvalid() || End.isInvalid()) {
133e5dd7070Spatrick       llvm::errs() << "error: -selection=" << Range.FileName << ':'
134e5dd7070Spatrick                    << Range.Begin.first << ':' << Range.Begin.second << '-'
135e5dd7070Spatrick                    << Range.End.first << ':' << Range.End.second
136e5dd7070Spatrick                    << " : invalid source location\n";
137e5dd7070Spatrick       return true;
138e5dd7070Spatrick     }
139e5dd7070Spatrick     Callback(SourceRange(Start, End));
140e5dd7070Spatrick     return false;
141e5dd7070Spatrick   }
142e5dd7070Spatrick 
143e5dd7070Spatrick private:
144e5dd7070Spatrick   ParsedSourceRange Range;
145e5dd7070Spatrick };
146e5dd7070Spatrick 
147e5dd7070Spatrick std::unique_ptr<SourceSelectionArgument>
fromString(StringRef Value)148e5dd7070Spatrick SourceSelectionArgument::fromString(StringRef Value) {
149e5dd7070Spatrick   if (Value.startswith("test:")) {
150e5dd7070Spatrick     StringRef Filename = Value.drop_front(strlen("test:"));
151*12c85518Srobert     std::optional<TestSelectionRangesInFile> ParsedTestSelection =
152e5dd7070Spatrick         findTestSelectionRanges(Filename);
153e5dd7070Spatrick     if (!ParsedTestSelection)
154e5dd7070Spatrick       return nullptr; // A parsing error was already reported.
155e5dd7070Spatrick     return std::make_unique<TestSourceSelectionArgument>(
156e5dd7070Spatrick         std::move(*ParsedTestSelection));
157e5dd7070Spatrick   }
158*12c85518Srobert   std::optional<ParsedSourceRange> Range = ParsedSourceRange::fromString(Value);
159e5dd7070Spatrick   if (Range)
160e5dd7070Spatrick     return std::make_unique<SourceRangeSelectionArgument>(std::move(*Range));
161e5dd7070Spatrick   llvm::errs() << "error: '-selection' option must be specified using "
162e5dd7070Spatrick                   "<file>:<line>:<column> or "
163e5dd7070Spatrick                   "<file>:<line>:<column>-<line>:<column> format\n";
164e5dd7070Spatrick   return nullptr;
165e5dd7070Spatrick }
166e5dd7070Spatrick 
167e5dd7070Spatrick /// A container that stores the command-line options used by a single
168e5dd7070Spatrick /// refactoring option.
169e5dd7070Spatrick class RefactoringActionCommandLineOptions {
170e5dd7070Spatrick public:
addStringOption(const RefactoringOption & Option,std::unique_ptr<cl::opt<std::string>> CLOption)171e5dd7070Spatrick   void addStringOption(const RefactoringOption &Option,
172e5dd7070Spatrick                        std::unique_ptr<cl::opt<std::string>> CLOption) {
173e5dd7070Spatrick     StringOptions[&Option] = std::move(CLOption);
174e5dd7070Spatrick   }
175e5dd7070Spatrick 
176e5dd7070Spatrick   const cl::opt<std::string> &
getStringOption(const RefactoringOption & Opt) const177e5dd7070Spatrick   getStringOption(const RefactoringOption &Opt) const {
178e5dd7070Spatrick     auto It = StringOptions.find(&Opt);
179e5dd7070Spatrick     return *It->second;
180e5dd7070Spatrick   }
181e5dd7070Spatrick 
182e5dd7070Spatrick private:
183e5dd7070Spatrick   llvm::DenseMap<const RefactoringOption *,
184e5dd7070Spatrick                  std::unique_ptr<cl::opt<std::string>>>
185e5dd7070Spatrick       StringOptions;
186e5dd7070Spatrick };
187e5dd7070Spatrick 
188e5dd7070Spatrick /// Passes the command-line option values to the options used by a single
189e5dd7070Spatrick /// refactoring action rule.
190e5dd7070Spatrick class CommandLineRefactoringOptionVisitor final
191e5dd7070Spatrick     : public RefactoringOptionVisitor {
192e5dd7070Spatrick public:
CommandLineRefactoringOptionVisitor(const RefactoringActionCommandLineOptions & Options)193e5dd7070Spatrick   CommandLineRefactoringOptionVisitor(
194e5dd7070Spatrick       const RefactoringActionCommandLineOptions &Options)
195e5dd7070Spatrick       : Options(Options) {}
196e5dd7070Spatrick 
visit(const RefactoringOption & Opt,std::optional<std::string> & Value)197e5dd7070Spatrick   void visit(const RefactoringOption &Opt,
198*12c85518Srobert              std::optional<std::string> &Value) override {
199e5dd7070Spatrick     const cl::opt<std::string> &CLOpt = Options.getStringOption(Opt);
200e5dd7070Spatrick     if (!CLOpt.getValue().empty()) {
201e5dd7070Spatrick       Value = CLOpt.getValue();
202e5dd7070Spatrick       return;
203e5dd7070Spatrick     }
204*12c85518Srobert     Value = std::nullopt;
205e5dd7070Spatrick     if (Opt.isRequired())
206e5dd7070Spatrick       MissingRequiredOptions.push_back(&Opt);
207e5dd7070Spatrick   }
208e5dd7070Spatrick 
getMissingRequiredOptions() const209e5dd7070Spatrick   ArrayRef<const RefactoringOption *> getMissingRequiredOptions() const {
210e5dd7070Spatrick     return MissingRequiredOptions;
211e5dd7070Spatrick   }
212e5dd7070Spatrick 
213e5dd7070Spatrick private:
214e5dd7070Spatrick   llvm::SmallVector<const RefactoringOption *, 4> MissingRequiredOptions;
215e5dd7070Spatrick   const RefactoringActionCommandLineOptions &Options;
216e5dd7070Spatrick };
217e5dd7070Spatrick 
218e5dd7070Spatrick /// Creates the refactoring options used by all the rules in a single
219e5dd7070Spatrick /// refactoring action.
220e5dd7070Spatrick class CommandLineRefactoringOptionCreator final
221e5dd7070Spatrick     : public RefactoringOptionVisitor {
222e5dd7070Spatrick public:
CommandLineRefactoringOptionCreator(cl::OptionCategory & Category,cl::SubCommand & Subcommand,RefactoringActionCommandLineOptions & Options)223e5dd7070Spatrick   CommandLineRefactoringOptionCreator(
224e5dd7070Spatrick       cl::OptionCategory &Category, cl::SubCommand &Subcommand,
225e5dd7070Spatrick       RefactoringActionCommandLineOptions &Options)
226e5dd7070Spatrick       : Category(Category), Subcommand(Subcommand), Options(Options) {}
227e5dd7070Spatrick 
visit(const RefactoringOption & Opt,std::optional<std::string> &)228*12c85518Srobert   void visit(const RefactoringOption &Opt,
229*12c85518Srobert              std::optional<std::string> &) override {
230e5dd7070Spatrick     if (Visited.insert(&Opt).second)
231e5dd7070Spatrick       Options.addStringOption(Opt, create<std::string>(Opt));
232e5dd7070Spatrick   }
233e5dd7070Spatrick 
234e5dd7070Spatrick private:
235e5dd7070Spatrick   template <typename T>
create(const RefactoringOption & Opt)236e5dd7070Spatrick   std::unique_ptr<cl::opt<T>> create(const RefactoringOption &Opt) {
237e5dd7070Spatrick     if (!OptionNames.insert(Opt.getName()).second)
238e5dd7070Spatrick       llvm::report_fatal_error("Multiple identical refactoring options "
239e5dd7070Spatrick                                "specified for one refactoring action");
240e5dd7070Spatrick     // FIXME: cl::Required can be specified when this option is present
241e5dd7070Spatrick     // in all rules in an action.
242e5dd7070Spatrick     return std::make_unique<cl::opt<T>>(
243e5dd7070Spatrick         Opt.getName(), cl::desc(Opt.getDescription()), cl::Optional,
244e5dd7070Spatrick         cl::cat(Category), cl::sub(Subcommand));
245e5dd7070Spatrick   }
246e5dd7070Spatrick 
247e5dd7070Spatrick   llvm::SmallPtrSet<const RefactoringOption *, 8> Visited;
248e5dd7070Spatrick   llvm::StringSet<> OptionNames;
249e5dd7070Spatrick   cl::OptionCategory &Category;
250e5dd7070Spatrick   cl::SubCommand &Subcommand;
251e5dd7070Spatrick   RefactoringActionCommandLineOptions &Options;
252e5dd7070Spatrick };
253e5dd7070Spatrick 
254e5dd7070Spatrick /// A subcommand that corresponds to individual refactoring action.
255e5dd7070Spatrick class RefactoringActionSubcommand : public cl::SubCommand {
256e5dd7070Spatrick public:
RefactoringActionSubcommand(std::unique_ptr<RefactoringAction> Action,RefactoringActionRules ActionRules,cl::OptionCategory & Category)257e5dd7070Spatrick   RefactoringActionSubcommand(std::unique_ptr<RefactoringAction> Action,
258e5dd7070Spatrick                               RefactoringActionRules ActionRules,
259e5dd7070Spatrick                               cl::OptionCategory &Category)
260e5dd7070Spatrick       : SubCommand(Action->getCommand(), Action->getDescription()),
261e5dd7070Spatrick         Action(std::move(Action)), ActionRules(std::move(ActionRules)) {
262e5dd7070Spatrick     // Check if the selection option is supported.
263e5dd7070Spatrick     for (const auto &Rule : this->ActionRules) {
264e5dd7070Spatrick       if (Rule->hasSelectionRequirement()) {
265e5dd7070Spatrick         Selection = std::make_unique<cl::opt<std::string>>(
266e5dd7070Spatrick             "selection",
267e5dd7070Spatrick             cl::desc(
268e5dd7070Spatrick                 "The selected source range in which the refactoring should "
269e5dd7070Spatrick                 "be initiated (<file>:<line>:<column>-<line>:<column> or "
270e5dd7070Spatrick                 "<file>:<line>:<column>)"),
271e5dd7070Spatrick             cl::cat(Category), cl::sub(*this));
272e5dd7070Spatrick         break;
273e5dd7070Spatrick       }
274e5dd7070Spatrick     }
275e5dd7070Spatrick     // Create the refactoring options.
276e5dd7070Spatrick     for (const auto &Rule : this->ActionRules) {
277e5dd7070Spatrick       CommandLineRefactoringOptionCreator OptionCreator(Category, *this,
278e5dd7070Spatrick                                                         Options);
279e5dd7070Spatrick       Rule->visitRefactoringOptions(OptionCreator);
280e5dd7070Spatrick     }
281e5dd7070Spatrick   }
282e5dd7070Spatrick 
~RefactoringActionSubcommand()283e5dd7070Spatrick   ~RefactoringActionSubcommand() { unregisterSubCommand(); }
284e5dd7070Spatrick 
getActionRules() const285e5dd7070Spatrick   const RefactoringActionRules &getActionRules() const { return ActionRules; }
286e5dd7070Spatrick 
287e5dd7070Spatrick   /// Parses the "-selection" command-line argument.
288e5dd7070Spatrick   ///
289e5dd7070Spatrick   /// \returns true on error, false otherwise.
parseSelectionArgument()290e5dd7070Spatrick   bool parseSelectionArgument() {
291e5dd7070Spatrick     if (Selection) {
292e5dd7070Spatrick       ParsedSelection = SourceSelectionArgument::fromString(*Selection);
293e5dd7070Spatrick       if (!ParsedSelection)
294e5dd7070Spatrick         return true;
295e5dd7070Spatrick     }
296e5dd7070Spatrick     return false;
297e5dd7070Spatrick   }
298e5dd7070Spatrick 
getSelection() const299e5dd7070Spatrick   SourceSelectionArgument *getSelection() const {
300e5dd7070Spatrick     assert(Selection && "selection not supported!");
301e5dd7070Spatrick     return ParsedSelection.get();
302e5dd7070Spatrick   }
303e5dd7070Spatrick 
getOptions() const304e5dd7070Spatrick   const RefactoringActionCommandLineOptions &getOptions() const {
305e5dd7070Spatrick     return Options;
306e5dd7070Spatrick   }
307e5dd7070Spatrick 
308e5dd7070Spatrick private:
309e5dd7070Spatrick   std::unique_ptr<RefactoringAction> Action;
310e5dd7070Spatrick   RefactoringActionRules ActionRules;
311e5dd7070Spatrick   std::unique_ptr<cl::opt<std::string>> Selection;
312e5dd7070Spatrick   std::unique_ptr<SourceSelectionArgument> ParsedSelection;
313e5dd7070Spatrick   RefactoringActionCommandLineOptions Options;
314e5dd7070Spatrick };
315e5dd7070Spatrick 
316e5dd7070Spatrick class ClangRefactorConsumer final : public ClangRefactorToolConsumerInterface {
317e5dd7070Spatrick public:
ClangRefactorConsumer(AtomicChanges & Changes)318e5dd7070Spatrick   ClangRefactorConsumer(AtomicChanges &Changes) : SourceChanges(&Changes) {}
319e5dd7070Spatrick 
handleError(llvm::Error Err)320e5dd7070Spatrick   void handleError(llvm::Error Err) override {
321*12c85518Srobert     std::optional<PartialDiagnosticAt> Diag = DiagnosticError::take(Err);
322e5dd7070Spatrick     if (!Diag) {
323e5dd7070Spatrick       llvm::errs() << llvm::toString(std::move(Err)) << "\n";
324e5dd7070Spatrick       return;
325e5dd7070Spatrick     }
326e5dd7070Spatrick     llvm::cantFail(std::move(Err)); // This is a success.
327e5dd7070Spatrick     DiagnosticBuilder DB(
328e5dd7070Spatrick         getDiags().Report(Diag->first, Diag->second.getDiagID()));
329e5dd7070Spatrick     Diag->second.Emit(DB);
330e5dd7070Spatrick   }
331e5dd7070Spatrick 
handle(AtomicChanges Changes)332e5dd7070Spatrick   void handle(AtomicChanges Changes) override {
333e5dd7070Spatrick     SourceChanges->insert(SourceChanges->begin(), Changes.begin(),
334e5dd7070Spatrick                           Changes.end());
335e5dd7070Spatrick   }
336e5dd7070Spatrick 
handle(SymbolOccurrences Occurrences)337e5dd7070Spatrick   void handle(SymbolOccurrences Occurrences) override {
338e5dd7070Spatrick     llvm_unreachable("symbol occurrence results are not handled yet");
339e5dd7070Spatrick   }
340e5dd7070Spatrick 
341e5dd7070Spatrick private:
342e5dd7070Spatrick   AtomicChanges *SourceChanges;
343e5dd7070Spatrick };
344e5dd7070Spatrick 
345e5dd7070Spatrick class ClangRefactorTool {
346e5dd7070Spatrick public:
ClangRefactorTool()347e5dd7070Spatrick   ClangRefactorTool()
348e5dd7070Spatrick       : SelectedSubcommand(nullptr), MatchingRule(nullptr),
349e5dd7070Spatrick         Consumer(new ClangRefactorConsumer(Changes)), HasFailed(false) {
350e5dd7070Spatrick     std::vector<std::unique_ptr<RefactoringAction>> Actions =
351e5dd7070Spatrick         createRefactoringActions();
352e5dd7070Spatrick 
353e5dd7070Spatrick     // Actions must have unique command names so that we can map them to one
354e5dd7070Spatrick     // subcommand.
355e5dd7070Spatrick     llvm::StringSet<> CommandNames;
356e5dd7070Spatrick     for (const auto &Action : Actions) {
357e5dd7070Spatrick       if (!CommandNames.insert(Action->getCommand()).second) {
358e5dd7070Spatrick         llvm::errs() << "duplicate refactoring action command '"
359e5dd7070Spatrick                      << Action->getCommand() << "'!";
360e5dd7070Spatrick         exit(1);
361e5dd7070Spatrick       }
362e5dd7070Spatrick     }
363e5dd7070Spatrick 
364e5dd7070Spatrick     // Create subcommands and command-line options.
365e5dd7070Spatrick     for (auto &Action : Actions) {
366e5dd7070Spatrick       SubCommands.push_back(std::make_unique<RefactoringActionSubcommand>(
367e5dd7070Spatrick           std::move(Action), Action->createActiveActionRules(),
368e5dd7070Spatrick           opts::CommonRefactorOptions));
369e5dd7070Spatrick     }
370e5dd7070Spatrick   }
371e5dd7070Spatrick 
372e5dd7070Spatrick   // Initializes the selected subcommand and refactoring rule based on the
373e5dd7070Spatrick   // command line options.
Init()374e5dd7070Spatrick   llvm::Error Init() {
375e5dd7070Spatrick     auto Subcommand = getSelectedSubcommand();
376e5dd7070Spatrick     if (!Subcommand)
377e5dd7070Spatrick       return Subcommand.takeError();
378e5dd7070Spatrick     auto Rule = getMatchingRule(**Subcommand);
379e5dd7070Spatrick     if (!Rule)
380e5dd7070Spatrick       return Rule.takeError();
381e5dd7070Spatrick 
382e5dd7070Spatrick     SelectedSubcommand = *Subcommand;
383e5dd7070Spatrick     MatchingRule = *Rule;
384e5dd7070Spatrick 
385e5dd7070Spatrick     return llvm::Error::success();
386e5dd7070Spatrick   }
387e5dd7070Spatrick 
hasFailed() const388e5dd7070Spatrick   bool hasFailed() const { return HasFailed; }
389e5dd7070Spatrick 
390e5dd7070Spatrick   using TUCallbackType = std::function<void(ASTContext &)>;
391e5dd7070Spatrick 
392e5dd7070Spatrick   // Callback of an AST action. This invokes the matching rule on the given AST.
callback(ASTContext & AST)393e5dd7070Spatrick   void callback(ASTContext &AST) {
394e5dd7070Spatrick     assert(SelectedSubcommand && MatchingRule && Consumer);
395e5dd7070Spatrick     RefactoringRuleContext Context(AST.getSourceManager());
396e5dd7070Spatrick     Context.setASTContext(AST);
397e5dd7070Spatrick 
398e5dd7070Spatrick     // If the selection option is test specific, we use a test-specific
399e5dd7070Spatrick     // consumer.
400e5dd7070Spatrick     std::unique_ptr<ClangRefactorToolConsumerInterface> TestConsumer;
401e5dd7070Spatrick     bool HasSelection = MatchingRule->hasSelectionRequirement();
402e5dd7070Spatrick     if (HasSelection)
403e5dd7070Spatrick       TestConsumer = SelectedSubcommand->getSelection()->createCustomConsumer();
404e5dd7070Spatrick     ClangRefactorToolConsumerInterface *ActiveConsumer =
405e5dd7070Spatrick         TestConsumer ? TestConsumer.get() : Consumer.get();
406e5dd7070Spatrick     ActiveConsumer->beginTU(AST);
407e5dd7070Spatrick 
408e5dd7070Spatrick     auto InvokeRule = [&](RefactoringResultConsumer &Consumer) {
409e5dd7070Spatrick       if (opts::Verbose)
410e5dd7070Spatrick         logInvocation(*SelectedSubcommand, Context);
411e5dd7070Spatrick       MatchingRule->invoke(*ActiveConsumer, Context);
412e5dd7070Spatrick     };
413e5dd7070Spatrick     if (HasSelection) {
414e5dd7070Spatrick       assert(SelectedSubcommand->getSelection() &&
415e5dd7070Spatrick              "Missing selection argument?");
416e5dd7070Spatrick       if (opts::Verbose)
417e5dd7070Spatrick         SelectedSubcommand->getSelection()->print(llvm::outs());
418e5dd7070Spatrick       if (SelectedSubcommand->getSelection()->forAllRanges(
419e5dd7070Spatrick               Context.getSources(), [&](SourceRange R) {
420e5dd7070Spatrick                 Context.setSelectionRange(R);
421e5dd7070Spatrick                 InvokeRule(*ActiveConsumer);
422e5dd7070Spatrick               }))
423e5dd7070Spatrick         HasFailed = true;
424e5dd7070Spatrick       ActiveConsumer->endTU();
425e5dd7070Spatrick       return;
426e5dd7070Spatrick     }
427e5dd7070Spatrick     InvokeRule(*ActiveConsumer);
428e5dd7070Spatrick     ActiveConsumer->endTU();
429e5dd7070Spatrick   }
430e5dd7070Spatrick 
431e5dd7070Spatrick   llvm::Expected<std::unique_ptr<FrontendActionFactory>>
getFrontendActionFactory()432e5dd7070Spatrick   getFrontendActionFactory() {
433e5dd7070Spatrick     class ToolASTConsumer : public ASTConsumer {
434e5dd7070Spatrick     public:
435e5dd7070Spatrick       TUCallbackType Callback;
436e5dd7070Spatrick       ToolASTConsumer(TUCallbackType Callback)
437e5dd7070Spatrick           : Callback(std::move(Callback)) {}
438e5dd7070Spatrick 
439e5dd7070Spatrick       void HandleTranslationUnit(ASTContext &Context) override {
440e5dd7070Spatrick         Callback(Context);
441e5dd7070Spatrick       }
442e5dd7070Spatrick     };
443e5dd7070Spatrick     class ToolASTAction : public ASTFrontendAction {
444e5dd7070Spatrick     public:
445e5dd7070Spatrick       explicit ToolASTAction(TUCallbackType Callback)
446e5dd7070Spatrick           : Callback(std::move(Callback)) {}
447e5dd7070Spatrick 
448e5dd7070Spatrick     protected:
449e5dd7070Spatrick       std::unique_ptr<clang::ASTConsumer>
450e5dd7070Spatrick       CreateASTConsumer(clang::CompilerInstance &compiler,
451e5dd7070Spatrick                         StringRef /* dummy */) override {
452e5dd7070Spatrick         std::unique_ptr<clang::ASTConsumer> Consumer{
453e5dd7070Spatrick             new ToolASTConsumer(Callback)};
454e5dd7070Spatrick         return Consumer;
455e5dd7070Spatrick       }
456e5dd7070Spatrick 
457e5dd7070Spatrick     private:
458e5dd7070Spatrick       TUCallbackType Callback;
459e5dd7070Spatrick     };
460e5dd7070Spatrick 
461e5dd7070Spatrick     class ToolActionFactory : public FrontendActionFactory {
462e5dd7070Spatrick     public:
463e5dd7070Spatrick       ToolActionFactory(TUCallbackType Callback)
464e5dd7070Spatrick           : Callback(std::move(Callback)) {}
465e5dd7070Spatrick 
466e5dd7070Spatrick       std::unique_ptr<FrontendAction> create() override {
467e5dd7070Spatrick         return std::make_unique<ToolASTAction>(Callback);
468e5dd7070Spatrick       }
469e5dd7070Spatrick 
470e5dd7070Spatrick     private:
471e5dd7070Spatrick       TUCallbackType Callback;
472e5dd7070Spatrick     };
473e5dd7070Spatrick 
474e5dd7070Spatrick     return std::make_unique<ToolActionFactory>(
475e5dd7070Spatrick         [this](ASTContext &AST) { return callback(AST); });
476e5dd7070Spatrick   }
477e5dd7070Spatrick 
478e5dd7070Spatrick   // FIXME(ioeric): this seems to only works for changes in a single file at
479e5dd7070Spatrick   // this point.
applySourceChanges()480e5dd7070Spatrick   bool applySourceChanges() {
481e5dd7070Spatrick     std::set<std::string> Files;
482e5dd7070Spatrick     for (const auto &Change : Changes)
483e5dd7070Spatrick       Files.insert(Change.getFilePath());
484e5dd7070Spatrick     // FIXME: Add automatic formatting support as well.
485e5dd7070Spatrick     tooling::ApplyChangesSpec Spec;
486e5dd7070Spatrick     // FIXME: We should probably cleanup the result by default as well.
487e5dd7070Spatrick     Spec.Cleanup = false;
488e5dd7070Spatrick     for (const auto &File : Files) {
489e5dd7070Spatrick       llvm::ErrorOr<std::unique_ptr<llvm::MemoryBuffer>> BufferErr =
490e5dd7070Spatrick           llvm::MemoryBuffer::getFile(File);
491e5dd7070Spatrick       if (!BufferErr) {
492e5dd7070Spatrick         llvm::errs() << "error: failed to open " << File << " for rewriting\n";
493e5dd7070Spatrick         return true;
494e5dd7070Spatrick       }
495e5dd7070Spatrick       auto Result = tooling::applyAtomicChanges(File, (*BufferErr)->getBuffer(),
496e5dd7070Spatrick                                                 Changes, Spec);
497e5dd7070Spatrick       if (!Result) {
498e5dd7070Spatrick         llvm::errs() << toString(Result.takeError());
499e5dd7070Spatrick         return true;
500e5dd7070Spatrick       }
501e5dd7070Spatrick 
502e5dd7070Spatrick       if (opts::Inplace) {
503e5dd7070Spatrick         std::error_code EC;
504a9ac8606Spatrick         llvm::raw_fd_ostream OS(File, EC, llvm::sys::fs::OF_TextWithCRLF);
505e5dd7070Spatrick         if (EC) {
506e5dd7070Spatrick           llvm::errs() << EC.message() << "\n";
507e5dd7070Spatrick           return true;
508e5dd7070Spatrick         }
509e5dd7070Spatrick         OS << *Result;
510e5dd7070Spatrick         continue;
511e5dd7070Spatrick       }
512e5dd7070Spatrick 
513e5dd7070Spatrick       llvm::outs() << *Result;
514e5dd7070Spatrick     }
515e5dd7070Spatrick     return false;
516e5dd7070Spatrick   }
517e5dd7070Spatrick 
518e5dd7070Spatrick private:
519e5dd7070Spatrick   /// Logs an individual refactoring action invocation to STDOUT.
logInvocation(RefactoringActionSubcommand & Subcommand,const RefactoringRuleContext & Context)520e5dd7070Spatrick   void logInvocation(RefactoringActionSubcommand &Subcommand,
521e5dd7070Spatrick                      const RefactoringRuleContext &Context) {
522e5dd7070Spatrick     llvm::outs() << "invoking action '" << Subcommand.getName() << "':\n";
523e5dd7070Spatrick     if (Context.getSelectionRange().isValid()) {
524e5dd7070Spatrick       SourceRange R = Context.getSelectionRange();
525e5dd7070Spatrick       llvm::outs() << "  -selection=";
526e5dd7070Spatrick       R.getBegin().print(llvm::outs(), Context.getSources());
527e5dd7070Spatrick       llvm::outs() << " -> ";
528e5dd7070Spatrick       R.getEnd().print(llvm::outs(), Context.getSources());
529e5dd7070Spatrick       llvm::outs() << "\n";
530e5dd7070Spatrick     }
531e5dd7070Spatrick   }
532e5dd7070Spatrick 
533e5dd7070Spatrick   llvm::Expected<RefactoringActionRule *>
getMatchingRule(RefactoringActionSubcommand & Subcommand)534e5dd7070Spatrick   getMatchingRule(RefactoringActionSubcommand &Subcommand) {
535e5dd7070Spatrick     SmallVector<RefactoringActionRule *, 4> MatchingRules;
536e5dd7070Spatrick     llvm::StringSet<> MissingOptions;
537e5dd7070Spatrick 
538e5dd7070Spatrick     for (const auto &Rule : Subcommand.getActionRules()) {
539e5dd7070Spatrick       CommandLineRefactoringOptionVisitor Visitor(Subcommand.getOptions());
540e5dd7070Spatrick       Rule->visitRefactoringOptions(Visitor);
541e5dd7070Spatrick       if (Visitor.getMissingRequiredOptions().empty()) {
542e5dd7070Spatrick         if (!Rule->hasSelectionRequirement()) {
543e5dd7070Spatrick           MatchingRules.push_back(Rule.get());
544e5dd7070Spatrick         } else {
545e5dd7070Spatrick           Subcommand.parseSelectionArgument();
546e5dd7070Spatrick           if (Subcommand.getSelection()) {
547e5dd7070Spatrick             MatchingRules.push_back(Rule.get());
548e5dd7070Spatrick           } else {
549e5dd7070Spatrick             MissingOptions.insert("selection");
550e5dd7070Spatrick           }
551e5dd7070Spatrick         }
552e5dd7070Spatrick       }
553e5dd7070Spatrick       for (const RefactoringOption *Opt : Visitor.getMissingRequiredOptions())
554e5dd7070Spatrick         MissingOptions.insert(Opt->getName());
555e5dd7070Spatrick     }
556e5dd7070Spatrick     if (MatchingRules.empty()) {
557e5dd7070Spatrick       std::string Error;
558e5dd7070Spatrick       llvm::raw_string_ostream OS(Error);
559e5dd7070Spatrick       OS << "ERROR: '" << Subcommand.getName()
560e5dd7070Spatrick          << "' can't be invoked with the given arguments:\n";
561e5dd7070Spatrick       for (const auto &Opt : MissingOptions)
562e5dd7070Spatrick         OS << "  missing '-" << Opt.getKey() << "' option\n";
563e5dd7070Spatrick       OS.flush();
564e5dd7070Spatrick       return llvm::make_error<llvm::StringError>(
565e5dd7070Spatrick           Error, llvm::inconvertibleErrorCode());
566e5dd7070Spatrick     }
567e5dd7070Spatrick     if (MatchingRules.size() != 1) {
568e5dd7070Spatrick       return llvm::make_error<llvm::StringError>(
569e5dd7070Spatrick           llvm::Twine("ERROR: more than one matching rule of action") +
570e5dd7070Spatrick               Subcommand.getName() + "was found with given options.",
571e5dd7070Spatrick           llvm::inconvertibleErrorCode());
572e5dd7070Spatrick     }
573e5dd7070Spatrick     return MatchingRules.front();
574e5dd7070Spatrick   }
575e5dd7070Spatrick   // Figure out which action is specified by the user. The user must specify the
576e5dd7070Spatrick   // action using a command-line subcommand, e.g. the invocation `clang-refactor
577e5dd7070Spatrick   // local-rename` corresponds to the `LocalRename` refactoring action. All
578e5dd7070Spatrick   // subcommands must have a unique names. This allows us to figure out which
579e5dd7070Spatrick   // refactoring action should be invoked by looking at the first subcommand
580e5dd7070Spatrick   // that's enabled by LLVM's command-line parser.
getSelectedSubcommand()581e5dd7070Spatrick   llvm::Expected<RefactoringActionSubcommand *> getSelectedSubcommand() {
582e5dd7070Spatrick     auto It = llvm::find_if(
583e5dd7070Spatrick         SubCommands,
584e5dd7070Spatrick         [](const std::unique_ptr<RefactoringActionSubcommand> &SubCommand) {
585e5dd7070Spatrick           return !!(*SubCommand);
586e5dd7070Spatrick         });
587e5dd7070Spatrick     if (It == SubCommands.end()) {
588e5dd7070Spatrick       std::string Error;
589e5dd7070Spatrick       llvm::raw_string_ostream OS(Error);
590e5dd7070Spatrick       OS << "error: no refactoring action given\n";
591e5dd7070Spatrick       OS << "note: the following actions are supported:\n";
592e5dd7070Spatrick       for (const auto &Subcommand : SubCommands)
593e5dd7070Spatrick         OS.indent(2) << Subcommand->getName() << "\n";
594e5dd7070Spatrick       OS.flush();
595e5dd7070Spatrick       return llvm::make_error<llvm::StringError>(
596e5dd7070Spatrick           Error, llvm::inconvertibleErrorCode());
597e5dd7070Spatrick     }
598e5dd7070Spatrick     RefactoringActionSubcommand *Subcommand = &(**It);
599e5dd7070Spatrick     return Subcommand;
600e5dd7070Spatrick   }
601e5dd7070Spatrick 
602e5dd7070Spatrick   std::vector<std::unique_ptr<RefactoringActionSubcommand>> SubCommands;
603e5dd7070Spatrick   RefactoringActionSubcommand *SelectedSubcommand;
604e5dd7070Spatrick   RefactoringActionRule *MatchingRule;
605e5dd7070Spatrick   std::unique_ptr<ClangRefactorToolConsumerInterface> Consumer;
606e5dd7070Spatrick   AtomicChanges Changes;
607e5dd7070Spatrick   bool HasFailed;
608e5dd7070Spatrick };
609e5dd7070Spatrick 
610e5dd7070Spatrick } // end anonymous namespace
611e5dd7070Spatrick 
main(int argc,const char ** argv)612e5dd7070Spatrick int main(int argc, const char **argv) {
613e5dd7070Spatrick   llvm::sys::PrintStackTraceOnErrorSignal(argv[0]);
614e5dd7070Spatrick 
615e5dd7070Spatrick   ClangRefactorTool RefactorTool;
616e5dd7070Spatrick 
617a9ac8606Spatrick   auto ExpectedParser = CommonOptionsParser::create(
618a9ac8606Spatrick       argc, argv, cl::getGeneralCategory(), cl::ZeroOrMore,
619e5dd7070Spatrick       "Clang-based refactoring tool for C, C++ and Objective-C");
620a9ac8606Spatrick   if (!ExpectedParser) {
621a9ac8606Spatrick     llvm::errs() << ExpectedParser.takeError();
622a9ac8606Spatrick     return 1;
623a9ac8606Spatrick   }
624a9ac8606Spatrick   CommonOptionsParser &Options = ExpectedParser.get();
625e5dd7070Spatrick 
626e5dd7070Spatrick   if (auto Err = RefactorTool.Init()) {
627e5dd7070Spatrick     llvm::errs() << llvm::toString(std::move(Err)) << "\n";
628e5dd7070Spatrick     return 1;
629e5dd7070Spatrick   }
630e5dd7070Spatrick 
631e5dd7070Spatrick   auto ActionFactory = RefactorTool.getFrontendActionFactory();
632e5dd7070Spatrick   if (!ActionFactory) {
633e5dd7070Spatrick     llvm::errs() << llvm::toString(ActionFactory.takeError()) << "\n";
634e5dd7070Spatrick     return 1;
635e5dd7070Spatrick   }
636e5dd7070Spatrick   ClangTool Tool(Options.getCompilations(), Options.getSourcePathList());
637e5dd7070Spatrick   bool Failed = false;
638e5dd7070Spatrick   if (Tool.run(ActionFactory->get()) != 0) {
639e5dd7070Spatrick     llvm::errs() << "Failed to run refactoring action on files\n";
640e5dd7070Spatrick     // It is possible that TUs are broken while changes are generated correctly,
641e5dd7070Spatrick     // so we still try applying changes.
642e5dd7070Spatrick     Failed = true;
643e5dd7070Spatrick   }
644e5dd7070Spatrick   return RefactorTool.applySourceChanges() || Failed ||
645e5dd7070Spatrick          RefactorTool.hasFailed();
646e5dd7070Spatrick }
647