Coverage Report

Created: 2023-11-11 10:31

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