Coverage Report

Created: 2020-09-19 12:23

/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 <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
17
  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
3
  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
7
  createCustomConsumer() {
76
7
    return nullptr;
77
7
  }
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
10
      : TestSelections(std::move(TestSelections)) {}
92
93
1
  void print(raw_ostream &OS) override { TestSelections.dump(OS); }
94
95
  std::unique_ptr<ClangRefactorToolConsumerInterface>
96
10
  createCustomConsumer() override {
97
10
    return TestSelections.createConsumer();
98
10
  }
99
100
  /// Testing support: invokes the selection action for each selection range in
101
  /// the test file.
102
  bool forAllRanges(const SourceManager &SM,
103
10
                    llvm::function_ref<void(SourceRange R)> Callback) override {
104
10
    return TestSelections.foreachRange(SM, Callback);
105
10
  }
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
7
      : Range(std::move(Range)) {}
116
117
  bool forAllRanges(const SourceManager &SM,
118
7
                    llvm::function_ref<void(SourceRange R)> Callback) override {
119
7
    auto FE = SM.getFileManager().getFile(Range.FileName);
120
7
    FileID FID = FE ? SM.translateFile(*FE) : 
FileID()0
;
121
7
    if (!FE || FID.isInvalid()) {
122
1
      llvm::errs() << "error: -selection=" << Range.FileName
123
1
                   << ":... : given file is not in the target TU\n";
124
1
      return true;
125
1
    }
126
6
127
6
    SourceLocation Start = SM.getMacroArgExpandedLocation(
128
6
        SM.translateLineCol(FID, Range.Begin.first, Range.Begin.second));
129
6
    SourceLocation End = SM.getMacroArgExpandedLocation(
130
6
        SM.translateLineCol(FID, Range.End.first, Range.End.second));
131
6
    if (Start.isInvalid() || End.isInvalid()) {
132
0
      llvm::errs() << "error: -selection=" << Range.FileName << ':'
133
0
                   << Range.Begin.first << ':' << Range.Begin.second << '-'
134
0
                   << Range.End.first << ':' << Range.End.second
135
0
                   << " : invalid source location\n";
136
0
      return true;
137
0
    }
138
6
    Callback(SourceRange(Start, End));
139
6
    return false;
140
6
  }
141
142
private:
143
  ParsedSourceRange Range;
144
};
145
146
std::unique_ptr<SourceSelectionArgument>
147
17
SourceSelectionArgument::fromString(StringRef Value) {
148
17
  if (Value.startswith("test:")) {
149
10
    StringRef Filename = Value.drop_front(strlen("test:"));
150
10
    Optional<TestSelectionRangesInFile> ParsedTestSelection =
151
10
        findTestSelectionRanges(Filename);
152
10
    if (!ParsedTestSelection)
153
0
      return nullptr; // A parsing error was already reported.
154
10
    return std::make_unique<TestSourceSelectionArgument>(
155
10
        std::move(*ParsedTestSelection));
156
10
  }
157
7
  Optional<ParsedSourceRange> Range = ParsedSourceRange::fromString(Value);
158
7
  if (Range)
159
7
    return std::make_unique<SourceRangeSelectionArgument>(std::move(*Range));
160
0
  llvm::errs() << "error: '-selection' option must be specified using "
161
0
                  "<file>:<line>:<column> or "
162
0
                  "<file>:<line>:<column>-<line>:<column> format\n";
163
0
  return nullptr;
164
0
}
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
76
                       std::unique_ptr<cl::opt<std::string>> CLOption) {
172
76
    StringOptions[&Option] = std::move(CLOption);
173
76
  }
174
175
  const cl::opt<std::string> &
176
42
  getStringOption(const RefactoringOption &Opt) const {
177
42
    auto It = StringOptions.find(&Opt);
178
42
    return *It->second;
179
42
  }
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
30
      : Options(Options) {}
195
196
  void visit(const RefactoringOption &Opt,
197
42
             Optional<std::string> &Value) override {
198
42
    const cl::opt<std::string> &CLOpt = Options.getStringOption(Opt);
199
42
    if (!CLOpt.getValue().empty()) {
200
13
      Value = CLOpt.getValue();
201
13
      return;
202
13
    }
203
29
    Value = None;
204
29
    if (Opt.isRequired())
205
23
      MissingRequiredOptions.push_back(&Opt);
206
29
  }
207
208
60
  ArrayRef<const RefactoringOption *> getMissingRequiredOptions() const {
209
60
    return MissingRequiredOptions;
210
60
  }
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
57
      : Category(Category), Subcommand(Subcommand), Options(Options) {}
226
227
76
  void visit(const RefactoringOption &Opt, Optional<std::string> &) override {
228
76
    if (Visited.insert(&Opt).second)
229
76
      Options.addStringOption(Opt, create<std::string>(Opt));
230
76
  }
231
232
private:
233
  template <typename T>
234
76
  std::unique_ptr<cl::opt<T>> create(const RefactoringOption &Opt) {
235
76
    if (!OptionNames.insert(Opt.getName()).second)
236
0
      llvm::report_fatal_error("Multiple identical refactoring options "
237
0
                               "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
76
    return std::make_unique<cl::opt<T>>(
241
76
        Opt.getName(), cl::desc(Opt.getDescription()), cl::Optional,
242
76
        cl::cat(Category), cl::sub(Subcommand));
243
76
  }
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
38
        Action(std::move(Action)), ActionRules(std::move(ActionRules)) {
260
    // Check if the selection option is supported.
261
38
    for (const auto &Rule : this->ActionRules) {
262
38
      if (Rule->hasSelectionRequirement()) {
263
38
        Selection = std::make_unique<cl::opt<std::string>>(
264
38
            "selection",
265
38
            cl::desc(
266
38
                "The selected source range in which the refactoring should "
267
38
                "be initiated (<file>:<line>:<column>-<line>:<column> or "
268
38
                "<file>:<line>:<column>)"),
269
38
            cl::cat(Category), cl::sub(*this));
270
38
        break;
271
38
      }
272
38
    }
273
    // Create the refactoring options.
274
57
    for (const auto &Rule : this->ActionRules) {
275
57
      CommandLineRefactoringOptionCreator OptionCreator(Category, *this,
276
57
                                                        Options);
277
57
      Rule->visitRefactoringOptions(OptionCreator);
278
57
    }
279
38
  }
280
281
38
  ~RefactoringActionSubcommand() { unregisterSubCommand(); }
282
283
18
  const RefactoringActionRules &getActionRules() const { return ActionRules; }
284
285
  /// Parses the "-selection" command-line argument.
286
  ///
287
  /// \returns true on error, false otherwise.
288
17
  bool parseSelectionArgument() {
289
17
    if (Selection) {
290
17
      ParsedSelection = SourceSelectionArgument::fromString(*Selection);
291
17
      if (!ParsedSelection)
292
0
        return true;
293
17
    }
294
17
    return false;
295
17
  }
296
297
72
  SourceSelectionArgument *getSelection() const {
298
72
    assert(Selection && "selection not supported!");
299
72
    return ParsedSelection.get();
300
72
  }
301
302
30
  const RefactoringActionCommandLineOptions &getOptions() const {
303
30
    return Options;
304
30
  }
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
19
  ClangRefactorConsumer(AtomicChanges &Changes) : SourceChanges(&Changes) {}
317
318
1
  void handleError(llvm::Error Err) override {
319
1
    Optional<PartialDiagnosticAt> Diag = DiagnosticError::take(Err);
320
1
    if (!Diag) {
321
0
      llvm::errs() << llvm::toString(std::move(Err)) << "\n";
322
0
      return;
323
0
    }
324
1
    llvm::cantFail(std::move(Err)); // This is a success.
325
1
    DiagnosticBuilder DB(
326
1
        getDiags().Report(Diag->first, Diag->second.getDiagID()));
327
1
    Diag->second.Emit(DB);
328
1
  }
329
330
6
  void handle(AtomicChanges Changes) override {
331
6
    SourceChanges->insert(SourceChanges->begin(), Changes.begin(),
332
6
                          Changes.end());
333
6
  }
334
335
0
  void handle(SymbolOccurrences Occurrences) override {
336
0
    llvm_unreachable("symbol occurrence results are not handled yet");
337
0
  }
338
339
private:
340
  AtomicChanges *SourceChanges;
341
};
342
343
class ClangRefactorTool {
344
public:
345
  ClangRefactorTool()
346
      : SelectedSubcommand(nullptr), MatchingRule(nullptr),
347
19
        Consumer(new ClangRefactorConsumer(Changes)), HasFailed(false) {
348
19
    std::vector<std::unique_ptr<RefactoringAction>> Actions =
349
19
        createRefactoringActions();
350
19
351
    // Actions must have unique command names so that we can map them to one
352
    // subcommand.
353
19
    llvm::StringSet<> CommandNames;
354
38
    for (const auto &Action : Actions) {
355
38
      if (!CommandNames.insert(Action->getCommand()).second) {
356
0
        llvm::errs() << "duplicate refactoring action command '"
357
0
                     << Action->getCommand() << "'!";
358
0
        exit(1);
359
0
      }
360
38
    }
361
19
362
    // Create subcommands and command-line options.
363
38
    
for (auto &Action : Actions)19
{
364
38
      SubCommands.push_back(std::make_unique<RefactoringActionSubcommand>(
365
38
          std::move(Action), Action->createActiveActionRules(),
366
38
          opts::CommonRefactorOptions));
367
38
    }
368
19
  }
369
370
  // Initializes the selected subcommand and refactoring rule based on the
371
  // command line options.
372
19
  llvm::Error Init() {
373
19
    auto Subcommand = getSelectedSubcommand();
374
19
    if (!Subcommand)
375
1
      return Subcommand.takeError();
376
18
    auto Rule = getMatchingRule(**Subcommand);
377
18
    if (!Rule)
378
0
      return Rule.takeError();
379
18
380
18
    SelectedSubcommand = *Subcommand;
381
18
    MatchingRule = *Rule;
382
18
383
18
    return llvm::Error::success();
384
18
  }
385
386
17
  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
18
  void callback(ASTContext &AST) {
392
18
    assert(SelectedSubcommand && MatchingRule && Consumer);
393
18
    RefactoringRuleContext Context(AST.getSourceManager());
394
18
    Context.setASTContext(AST);
395
18
396
    // If the selection option is test specific, we use a test-specific
397
    // consumer.
398
18
    std::unique_ptr<ClangRefactorToolConsumerInterface> TestConsumer;
399
18
    bool HasSelection = MatchingRule->hasSelectionRequirement();
400
18
    if (HasSelection)
401
17
      TestConsumer = SelectedSubcommand->getSelection()->createCustomConsumer();
402
18
    ClangRefactorToolConsumerInterface *ActiveConsumer =
403
10
        TestConsumer ? TestConsumer.get() : 
Consumer.get()8
;
404
18
    ActiveConsumer->beginTU(AST);
405
18
406
63
    auto InvokeRule = [&](RefactoringResultConsumer &Consumer) {
407
63
      if (opts::Verbose)
408
8
        logInvocation(*SelectedSubcommand, Context);
409
63
      MatchingRule->invoke(*ActiveConsumer, Context);
410
63
    };
411
18
    if (HasSelection) {
412
17
      assert(SelectedSubcommand->getSelection() &&
413
17
             "Missing selection argument?");
414
17
      if (opts::Verbose)
415
4
        SelectedSubcommand->getSelection()->print(llvm::outs());
416
17
      if (SelectedSubcommand->getSelection()->forAllRanges(
417
62
              Context.getSources(), [&](SourceRange R) {
418
62
                Context.setSelectionRange(R);
419
62
                InvokeRule(*ActiveConsumer);
420
62
              }))
421
1
        HasFailed = true;
422
17
      ActiveConsumer->endTU();
423
17
      return;
424
17
    }
425
1
    InvokeRule(*ActiveConsumer);
426
1
    ActiveConsumer->endTU();
427
1
  }
428
429
  llvm::Expected<std::unique_ptr<FrontendActionFactory>>
430
18
  getFrontendActionFactory() {
431
18
    class ToolASTConsumer : public ASTConsumer {
432
18
    public:
433
18
      TUCallbackType Callback;
434
18
      ToolASTConsumer(TUCallbackType Callback)
435
18
          : Callback(std::move(Callback)) {}
436
18
437
18
      void HandleTranslationUnit(ASTContext &Context) override {
438
18
        Callback(Context);
439
18
      }
440
18
    };
441
18
    class ToolASTAction : public ASTFrontendAction {
442
18
    public:
443
18
      explicit ToolASTAction(TUCallbackType Callback)
444
18
          : Callback(std::move(Callback)) {}
445
18
446
18
    protected:
447
18
      std::unique_ptr<clang::ASTConsumer>
448
18
      CreateASTConsumer(clang::CompilerInstance &compiler,
449
18
                        StringRef /* dummy */) override {
450
18
        std::unique_ptr<clang::ASTConsumer> Consumer{
451
18
            new ToolASTConsumer(Callback)};
452
18
        return Consumer;
453
18
      }
454
18
455
18
    private:
456
18
      TUCallbackType Callback;
457
18
    };
458
18
459
18
    class ToolActionFactory : public FrontendActionFactory {
460
18
    public:
461
18
      ToolActionFactory(TUCallbackType Callback)
462
18
          : Callback(std::move(Callback)) {}
463
18
464
18
      std::unique_ptr<FrontendAction> create() override {
465
18
        return std::make_unique<ToolASTAction>(Callback);
466
18
      }
467
18
468
18
    private:
469
18
      TUCallbackType Callback;
470
18
    };
471
18
472
18
    return std::make_unique<ToolActionFactory>(
473
18
        [this](ASTContext &AST) { return callback(AST); });
474
18
  }
475
476
  // FIXME(ioeric): this seems to only works for changes in a single file at
477
  // this point.
478
18
  bool applySourceChanges() {
479
18
    std::set<std::string> Files;
480
18
    for (const auto &Change : Changes)
481
10
      Files.insert(Change.getFilePath());
482
    // FIXME: Add automatic formatting support as well.
483
18
    tooling::ApplyChangesSpec Spec;
484
    // FIXME: We should probably cleanup the result by default as well.
485
18
    Spec.Cleanup = false;
486
6
    for (const auto &File : Files) {
487
6
      llvm::ErrorOr<std::unique_ptr<llvm::MemoryBuffer>> BufferErr =
488
6
          llvm::MemoryBuffer::getFile(File);
489
6
      if (!BufferErr) {
490
0
        llvm::errs() << "error: failed to open " << File << " for rewriting\n";
491
0
        return true;
492
0
      }
493
6
      auto Result = tooling::applyAtomicChanges(File, (*BufferErr)->getBuffer(),
494
6
                                                Changes, Spec);
495
6
      if (!Result) {
496
0
        llvm::errs() << toString(Result.takeError());
497
0
        return true;
498
0
      }
499
6
500
6
      if (opts::Inplace) {
501
1
        std::error_code EC;
502
1
        llvm::raw_fd_ostream OS(File, EC, llvm::sys::fs::OF_Text);
503
1
        if (EC) {
504
0
          llvm::errs() << EC.message() << "\n";
505
0
          return true;
506
0
        }
507
1
        OS << *Result;
508
1
        continue;
509
1
      }
510
5
511
5
      llvm::outs() << *Result;
512
5
    }
513
18
    return false;
514
18
  }
515
516
private:
517
  /// Logs an individual refactoring action invocation to STDOUT.
518
  void logInvocation(RefactoringActionSubcommand &Subcommand,
519
8
                     const RefactoringRuleContext &Context) {
520
8
    llvm::outs() << "invoking action '" << Subcommand.getName() << "':\n";
521
8
    if (Context.getSelectionRange().isValid()) {
522
8
      SourceRange R = Context.getSelectionRange();
523
8
      llvm::outs() << "  -selection=";
524
8
      R.getBegin().print(llvm::outs(), Context.getSources());
525
8
      llvm::outs() << " -> ";
526
8
      R.getEnd().print(llvm::outs(), Context.getSources());
527
8
      llvm::outs() << "\n";
528
8
    }
529
8
  }
530
531
  llvm::Expected<RefactoringActionRule *>
532
18
  getMatchingRule(RefactoringActionSubcommand &Subcommand) {
533
18
    SmallVector<RefactoringActionRule *, 4> MatchingRules;
534
18
    llvm::StringSet<> MissingOptions;
535
18
536
30
    for (const auto &Rule : Subcommand.getActionRules()) {
537
30
      CommandLineRefactoringOptionVisitor Visitor(Subcommand.getOptions());
538
30
      Rule->visitRefactoringOptions(Visitor);
539
30
      if (Visitor.getMissingRequiredOptions().empty()) {
540
18
        if (!Rule->hasSelectionRequirement()) {
541
1
          MatchingRules.push_back(Rule.get());
542
17
        } else {
543
17
          Subcommand.parseSelectionArgument();
544
17
          if (Subcommand.getSelection()) {
545
17
            MatchingRules.push_back(Rule.get());
546
0
          } else {
547
0
            MissingOptions.insert("selection");
548
0
          }
549
17
        }
550
18
      }
551
30
      for (const RefactoringOption *Opt : Visitor.getMissingRequiredOptions())
552
23
        MissingOptions.insert(Opt->getName());
553
30
    }
554
18
    if (MatchingRules.empty()) {
555
0
      std::string Error;
556
0
      llvm::raw_string_ostream OS(Error);
557
0
      OS << "ERROR: '" << Subcommand.getName()
558
0
         << "' can't be invoked with the given arguments:\n";
559
0
      for (const auto &Opt : MissingOptions)
560
0
        OS << "  missing '-" << Opt.getKey() << "' option\n";
561
0
      OS.flush();
562
0
      return llvm::make_error<llvm::StringError>(
563
0
          Error, llvm::inconvertibleErrorCode());
564
0
    }
565
18
    if (MatchingRules.size() != 1) {
566
0
      return llvm::make_error<llvm::StringError>(
567
0
          llvm::Twine("ERROR: more than one matching rule of action") +
568
0
              Subcommand.getName() + "was found with given options.",
569
0
          llvm::inconvertibleErrorCode());
570
0
    }
571
18
    return MatchingRules.front();
572
18
  }
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
19
  llvm::Expected<RefactoringActionSubcommand *> getSelectedSubcommand() {
580
19
    auto It = llvm::find_if(
581
19
        SubCommands,
582
26
        [](const std::unique_ptr<RefactoringActionSubcommand> &SubCommand) {
583
26
          return !!(*SubCommand);
584
26
        });
585
19
    if (It == SubCommands.end()) {
586
1
      std::string Error;
587
1
      llvm::raw_string_ostream OS(Error);
588
1
      OS << "error: no refactoring action given\n";
589
1
      OS << "note: the following actions are supported:\n";
590
1
      for (const auto &Subcommand : SubCommands)
591
2
        OS.indent(2) << Subcommand->getName() << "\n";
592
1
      OS.flush();
593
1
      return llvm::make_error<llvm::StringError>(
594
1
          Error, llvm::inconvertibleErrorCode());
595
1
    }
596
18
    RefactoringActionSubcommand *Subcommand = &(**It);
597
18
    return Subcommand;
598
18
  }
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
19
int main(int argc, const char **argv) {
611
19
  llvm::sys::PrintStackTraceOnErrorSignal(argv[0]);
612
19
613
19
  ClangRefactorTool RefactorTool;
614
19
615
19
  CommonOptionsParser Options(
616
19
      argc, argv, cl::GeneralCategory, cl::ZeroOrMore,
617
19
      "Clang-based refactoring tool for C, C++ and Objective-C");
618
19
619
19
  if (auto Err = RefactorTool.Init()) {
620
1
    llvm::errs() << llvm::toString(std::move(Err)) << "\n";
621
1
    return 1;
622
1
  }
623
18
624
18
  auto ActionFactory = RefactorTool.getFrontendActionFactory();
625
18
  if (!ActionFactory) {
626
0
    llvm::errs() << llvm::toString(ActionFactory.takeError()) << "\n";
627
0
    return 1;
628
0
  }
629
18
  ClangTool Tool(Options.getCompilations(), Options.getSourcePathList());
630
18
  bool Failed = false;
631
18
  if (Tool.run(ActionFactory->get()) != 0) {
632
1
    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
1
    Failed = true;
636
1
  }
637
18
  return RefactorTool.applySourceChanges() || Failed ||
638
17
         RefactorTool.hasFailed();
639
18
}