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