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