1 | |
2 | |
3 | |
4 | |
5 | |
6 | |
7 | |
8 | |
9 | |
10 | |
11 | |
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 | } |
49 | |
50 | namespace { |
51 | |
52 | |
53 | class SourceSelectionArgument { |
54 | public: |
55 | virtual ~SourceSelectionArgument() {} |
56 | |
57 | |
58 | |
59 | |
60 | static std::unique_ptr<SourceSelectionArgument> fromString(StringRef Value); |
61 | |
62 | |
63 | |
64 | virtual void print(raw_ostream &OS) {} |
65 | |
66 | |
67 | |
68 | |
69 | |
70 | |
71 | |
72 | |
73 | |
74 | virtual std::unique_ptr<ClangRefactorToolConsumerInterface> |
75 | createCustomConsumer() { |
76 | return nullptr; |
77 | } |
78 | |
79 | |
80 | |
81 | |
82 | virtual bool |
83 | forAllRanges(const SourceManager &SM, |
84 | llvm::function_ref<void(SourceRange R)> Callback) = 0; |
85 | }; |
86 | |
87 | |
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 | |
101 | |
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 | |
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 | const FileEntry *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; |
154 | return llvm::make_unique<TestSourceSelectionArgument>( |
155 | std::move(*ParsedTestSelection)); |
156 | } |
157 | Optional<ParsedSourceRange> Range = ParsedSourceRange::fromString(Value); |
158 | if (Range) |
159 | return llvm::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 | |
167 | |
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 | |
188 | |
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 | |
218 | |
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 | |
239 | |
240 | return llvm::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 | |
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 | |
261 | for (const auto &Rule : this->ActionRules) { |
262 | if (Rule->hasSelectionRequirement()) { |
263 | Selection = llvm::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 | |
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 | |
286 | |
287 | |
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 | (0) . __assert_fail ("Selection && \"selection not supported!\"", "/home/seafit/code_projects/clang_source/clang/tools/clang-refactor/ClangRefactor.cpp", 298, __PRETTY_FUNCTION__))" file_link="../../../include/assert.h.html#88" macro="true">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)); |
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 | |
352 | |
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 | |
363 | for (auto &Action : Actions) { |
364 | SubCommands.push_back(llvm::make_unique<RefactoringActionSubcommand>( |
365 | std::move(Action), Action->createActiveActionRules(), |
366 | opts::CommonRefactorOptions)); |
367 | } |
368 | } |
369 | |
370 | |
371 | |
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 | |
391 | void callback(ASTContext &AST) { |
392 | assert(SelectedSubcommand && MatchingRule && Consumer); |
393 | RefactoringRuleContext Context(AST.getSourceManager()); |
394 | Context.setASTContext(AST); |
395 | |
396 | |
397 | |
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 | (0) . __assert_fail ("SelectedSubcommand->getSelection() && \"Missing selection argument?\"", "/home/seafit/code_projects/clang_source/clang/tools/clang-refactor/ClangRefactor.cpp", 413, __PRETTY_FUNCTION__))" file_link="../../../include/assert.h.html#88" macro="true">assert(SelectedSubcommand->getSelection() && |
413 | (0) . __assert_fail ("SelectedSubcommand->getSelection() && \"Missing selection argument?\"", "/home/seafit/code_projects/clang_source/clang/tools/clang-refactor/ClangRefactor.cpp", 413, __PRETTY_FUNCTION__))" file_link="../../../include/assert.h.html#88" macro="true"> "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 ) 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 | FrontendAction *create() override { return new ToolASTAction(Callback); } |
465 | |
466 | private: |
467 | TUCallbackType Callback; |
468 | }; |
469 | |
470 | return llvm::make_unique<ToolActionFactory>( |
471 | [this](ASTContext &AST) { return callback(AST); }); |
472 | } |
473 | |
474 | |
475 | |
476 | bool applySourceChanges() { |
477 | std::set<std::string> Files; |
478 | for (const auto &Change : Changes) |
479 | Files.insert(Change.getFilePath()); |
480 | |
481 | tooling::ApplyChangesSpec Spec; |
482 | |
483 | Spec.Cleanup = false; |
484 | for (const auto &File : Files) { |
485 | llvm::ErrorOr<std::unique_ptr<llvm::MemoryBuffer>> BufferErr = |
486 | llvm::MemoryBuffer::getFile(File); |
487 | if (!BufferErr) { |
488 | llvm::errs() << "error: failed to open " << File << " for rewriting\n"; |
489 | return true; |
490 | } |
491 | auto Result = tooling::applyAtomicChanges(File, (*BufferErr)->getBuffer(), |
492 | Changes, Spec); |
493 | if (!Result) { |
494 | llvm::errs() << toString(Result.takeError()); |
495 | return true; |
496 | } |
497 | |
498 | if (opts::Inplace) { |
499 | std::error_code EC; |
500 | llvm::raw_fd_ostream OS(File, EC, llvm::sys::fs::F_Text); |
501 | if (EC) { |
502 | llvm::errs() << EC.message() << "\n"; |
503 | return true; |
504 | } |
505 | OS << *Result; |
506 | continue; |
507 | } |
508 | |
509 | llvm::outs() << *Result; |
510 | } |
511 | return false; |
512 | } |
513 | |
514 | private: |
515 | |
516 | void logInvocation(RefactoringActionSubcommand &Subcommand, |
517 | const RefactoringRuleContext &Context) { |
518 | llvm::outs() << "invoking action '" << Subcommand.getName() << "':\n"; |
519 | if (Context.getSelectionRange().isValid()) { |
520 | SourceRange R = Context.getSelectionRange(); |
521 | llvm::outs() << " -selection="; |
522 | R.getBegin().print(llvm::outs(), Context.getSources()); |
523 | llvm::outs() << " -> "; |
524 | R.getEnd().print(llvm::outs(), Context.getSources()); |
525 | llvm::outs() << "\n"; |
526 | } |
527 | } |
528 | |
529 | llvm::Expected<RefactoringActionRule *> |
530 | getMatchingRule(RefactoringActionSubcommand &Subcommand) { |
531 | SmallVector<RefactoringActionRule *, 4> MatchingRules; |
532 | llvm::StringSet<> MissingOptions; |
533 | |
534 | for (const auto &Rule : Subcommand.getActionRules()) { |
535 | CommandLineRefactoringOptionVisitor Visitor(Subcommand.getOptions()); |
536 | Rule->visitRefactoringOptions(Visitor); |
537 | if (Visitor.getMissingRequiredOptions().empty()) { |
538 | if (!Rule->hasSelectionRequirement()) { |
539 | MatchingRules.push_back(Rule.get()); |
540 | } else { |
541 | Subcommand.parseSelectionArgument(); |
542 | if (Subcommand.getSelection()) { |
543 | MatchingRules.push_back(Rule.get()); |
544 | } else { |
545 | MissingOptions.insert("selection"); |
546 | } |
547 | } |
548 | } |
549 | for (const RefactoringOption *Opt : Visitor.getMissingRequiredOptions()) |
550 | MissingOptions.insert(Opt->getName()); |
551 | } |
552 | if (MatchingRules.empty()) { |
553 | std::string Error; |
554 | llvm::raw_string_ostream OS(Error); |
555 | OS << "ERROR: '" << Subcommand.getName() |
556 | << "' can't be invoked with the given arguments:\n"; |
557 | for (const auto &Opt : MissingOptions) |
558 | OS << " missing '-" << Opt.getKey() << "' option\n"; |
559 | OS.flush(); |
560 | return llvm::make_error<llvm::StringError>( |
561 | Error, llvm::inconvertibleErrorCode()); |
562 | } |
563 | if (MatchingRules.size() != 1) { |
564 | return llvm::make_error<llvm::StringError>( |
565 | llvm::Twine("ERROR: more than one matching rule of action") + |
566 | Subcommand.getName() + "was found with given options.", |
567 | llvm::inconvertibleErrorCode()); |
568 | } |
569 | return MatchingRules.front(); |
570 | } |
571 | |
572 | |
573 | |
574 | |
575 | |
576 | |
577 | llvm::Expected<RefactoringActionSubcommand *> getSelectedSubcommand() { |
578 | auto It = llvm::find_if( |
579 | SubCommands, |
580 | [](const std::unique_ptr<RefactoringActionSubcommand> &SubCommand) { |
581 | return !!(*SubCommand); |
582 | }); |
583 | if (It == SubCommands.end()) { |
584 | std::string Error; |
585 | llvm::raw_string_ostream OS(Error); |
586 | OS << "error: no refactoring action given\n"; |
587 | OS << "note: the following actions are supported:\n"; |
588 | for (const auto &Subcommand : SubCommands) |
589 | OS.indent(2) << Subcommand->getName() << "\n"; |
590 | OS.flush(); |
591 | return llvm::make_error<llvm::StringError>( |
592 | Error, llvm::inconvertibleErrorCode()); |
593 | } |
594 | RefactoringActionSubcommand *Subcommand = &(**It); |
595 | return Subcommand; |
596 | } |
597 | |
598 | std::vector<std::unique_ptr<RefactoringActionSubcommand>> SubCommands; |
599 | RefactoringActionSubcommand *SelectedSubcommand; |
600 | RefactoringActionRule *MatchingRule; |
601 | std::unique_ptr<ClangRefactorToolConsumerInterface> Consumer; |
602 | AtomicChanges Changes; |
603 | bool HasFailed; |
604 | }; |
605 | |
606 | } |
607 | |
608 | int main(int argc, const char **argv) { |
609 | llvm::sys::PrintStackTraceOnErrorSignal(argv[0]); |
610 | |
611 | ClangRefactorTool RefactorTool; |
612 | |
613 | CommonOptionsParser Options( |
614 | argc, argv, cl::GeneralCategory, cl::ZeroOrMore, |
615 | "Clang-based refactoring tool for C, C++ and Objective-C"); |
616 | |
617 | if (auto Err = RefactorTool.Init()) { |
618 | llvm::errs() << llvm::toString(std::move(Err)) << "\n"; |
619 | return 1; |
620 | } |
621 | |
622 | auto ActionFactory = RefactorTool.getFrontendActionFactory(); |
623 | if (!ActionFactory) { |
624 | llvm::errs() << llvm::toString(ActionFactory.takeError()) << "\n"; |
625 | return 1; |
626 | } |
627 | ClangTool Tool(Options.getCompilations(), Options.getSourcePathList()); |
628 | bool Failed = false; |
629 | if (Tool.run(ActionFactory->get()) != 0) { |
630 | llvm::errs() << "Failed to run refactoring action on files\n"; |
631 | |
632 | |
633 | Failed = true; |
634 | } |
635 | return RefactorTool.applySourceChanges() || Failed || |
636 | RefactorTool.hasFailed(); |
637 | } |
638 | |