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 } 640