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