Index: include/clang/Tooling/Refactoring/Rename/RenamingAction.h =================================================================== --- include/clang/Tooling/Refactoring/Rename/RenamingAction.h +++ include/clang/Tooling/Refactoring/Rename/RenamingAction.h @@ -47,7 +47,21 @@ bool PrintLocations; }; -class RenameOccurrences final : public SourceChangeRefactoringRule { +/// A refactoring action rule for USR-based rename. +class USRRenameRule : public SourceChangeRefactoringRule { +protected: + USRRenameRule(const NamedDecl *ND, std::string NewName) + : ND(ND), NewName(std::move(NewName)) { + assert(ND); + } + + // A NamedDecl which indentifies the the symbol being renamed. + const NamedDecl *ND; + // The new name to change the symbol to. + std::string NewName; +}; + +class RenameOccurrences final : public USRRenameRule { public: static Expected initiate(RefactoringRuleContext &Context, SourceRange SelectionRange, @@ -57,13 +71,27 @@ private: RenameOccurrences(const NamedDecl *ND, std::string NewName) - : ND(ND), NewName(std::move(NewName)) {} + : USRRenameRule(ND, std::move(NewName)) {} Expected createSourceReplacements(RefactoringRuleContext &Context) override; +}; - const NamedDecl *ND; - std::string NewName; +class QualifiedRenameRule final : public USRRenameRule { +public: + static Expected initiate(RefactoringRuleContext &Context, + std::string OldQualifiedName, + std::string NewQualifiedName); + + static const RefactoringDescriptor &describe(); + +private: + QualifiedRenameRule(const NamedDecl *ND, + std::string NewQualifiedName) + : USRRenameRule(ND, std::move(NewQualifiedName)) {} + + Expected + createSourceReplacements(RefactoringRuleContext &Context) override; }; /// Returns source replacements that correspond to the rename of the given Index: lib/Tooling/Refactoring/RefactoringActions.cpp =================================================================== --- lib/Tooling/Refactoring/RefactoringActions.cpp +++ lib/Tooling/Refactoring/RefactoringActions.cpp @@ -46,6 +46,22 @@ } }; +class OldQualifiedNameOption : public RequiredRefactoringOption { +public: + StringRef getName() const override { return "old-qualified-name"; } + StringRef getDescription() const override { + return "The old qualified name to be renamed"; + } +}; + +class NewQualifiedNameOption : public RequiredRefactoringOption { +public: + StringRef getName() const override { return "new-qualified-name"; } + StringRef getDescription() const override { + return "The new qualified name to change the symbol to"; + } +}; + class NewNameOption : public RequiredRefactoringOption { public: StringRef getName() const override { return "new-name"; } @@ -70,6 +86,10 @@ RefactoringActionRules Rules; Rules.push_back(createRefactoringActionRule( SourceRangeSelectionRequirement(), OptionRequirement())); + // FIXME: Use NewNameOption. + Rules.push_back(createRefactoringActionRule( + OptionRequirement(), + OptionRequirement())); return Rules; } }; Index: lib/Tooling/Refactoring/Rename/RenamingAction.cpp =================================================================== --- lib/Tooling/Refactoring/Rename/RenamingAction.cpp +++ lib/Tooling/Refactoring/Rename/RenamingAction.cpp @@ -31,6 +31,8 @@ #include "clang/Tooling/Refactoring/Rename/USRLocFinder.h" #include "clang/Tooling/Tooling.h" #include "llvm/ADT/STLExtras.h" +#include "llvm/Support/Errc.h" +#include "llvm/Support/Error.h" #include #include @@ -92,6 +94,37 @@ *Occurrences, Context.getASTContext().getSourceManager(), Name); } +Expected +QualifiedRenameRule::initiate(RefactoringRuleContext &Context, + std::string OldQualifiedName, + std::string NewQualifiedName) { + const NamedDecl *ND = + getNamedDeclFor(Context.getASTContext(), OldQualifiedName); + if (!ND) + return llvm::make_error("Could not find symbol " + + OldQualifiedName, + llvm::errc::invalid_argument); + return QualifiedRenameRule(ND, std::move(NewQualifiedName)); +} + +const RefactoringDescriptor &QualifiedRenameRule::describe() { + static const RefactoringDescriptor Descriptor = { + /*Name=*/"local-qualified-rename", + /*Title=*/"Qualified Rename", + /*Description=*/ + "Finds and renames qualified symbols in code with no indexer support", + }; + return Descriptor; +} + +Expected +QualifiedRenameRule::createSourceReplacements(RefactoringRuleContext &Context) { + auto USRs = getUSRsForDeclaration(ND, Context.getASTContext()); + assert(!USRs.empty()); + return tooling::createRenameAtomicChanges( + USRs, NewName, Context.getASTContext().getTranslationUnitDecl()); +} + Expected> createRenameReplacements(const SymbolOccurrences &Occurrences, const SourceManager &SM, const SymbolName &NewName) { Index: test/Refactor/LocalRename/QualifiedRename.cpp =================================================================== --- /dev/null +++ test/Refactor/LocalRename/QualifiedRename.cpp @@ -0,0 +1,24 @@ +// RUN: clang-refactor local-rename -old-qualified-name="foo::A" -new-qualified-name="bar::B" %s -- -std=c++11 2>&1 | grep -v CHECK | FileCheck %s + +namespace foo { +class A {}; +} +// CHECK: namespace foo { +// CHECK-NEXT: class B {}; +// CHECK-NEXT: } + +namespace bar { +void f(foo::A* a) { + foo::A b; +} +// CHECK: void f(B* a) { +// CHECK-NEXT: B b; +// CHECK-NEXT: } +} + +void f(foo::A* a) { + foo::A b; +} +// CHECK: void f(bar::B* a) { +// CHECK-NEXT: bar::B b; +// CHECK-NEXT: } Index: tools/clang-refactor/ClangRefactor.cpp =================================================================== --- tools/clang-refactor/ClangRefactor.cpp +++ tools/clang-refactor/ClangRefactor.cpp @@ -283,10 +283,10 @@ const RefactoringActionRules &getActionRules() const { return ActionRules; } - /// Parses the command-line arguments that are specific to this rule. + /// Parses the "-selection" command-line argument. /// /// \returns true on error, false otherwise. - bool parseArguments() { + bool parseSelectionArgument() { if (Selection) { ParsedSelection = SourceSelectionArgument::fromString(*Selection); if (!ParsedSelection) @@ -464,20 +464,20 @@ SmallVector MatchingRules; llvm::StringSet<> MissingOptions; - bool HasSelection = false; for (const auto &Rule : Subcommand.getActionRules()) { - bool SelectionMatches = true; - if (Rule->hasSelectionRequirement()) { - HasSelection = true; - if (!Subcommand.getSelection()) { - MissingOptions.insert("selection"); - SelectionMatches = false; - } - } CommandLineRefactoringOptionVisitor Visitor(Subcommand.getOptions()); Rule->visitRefactoringOptions(Visitor); - if (SelectionMatches && Visitor.getMissingRequiredOptions().empty()) { - MatchingRules.push_back(Rule.get()); + if (Visitor.getMissingRequiredOptions().empty()) { + if (!Rule->hasSelectionRequirement()) { + MatchingRules.push_back(Rule.get()); + } else { + Subcommand.parseSelectionArgument(); + if (Subcommand.getSelection()) { + MatchingRules.push_back(Rule.get()); + } else { + MissingOptions.insert("selection"); + } + } continue; } for (const RefactoringOption *Opt : Visitor.getMissingRequiredOptions()) @@ -490,6 +490,14 @@ llvm::errs() << " missing '-" << Opt.getKey() << "' option\n"; return true; } + if (MatchingRules.size() > 1) { + llvm::errs() << "error: find multiple matched action rules for the given " + "arguments\n"; + return true; + } + + RefactoringActionRule* MatchingRule = MatchingRules[0]; + bool HasSelection = MatchingRule->hasSelectionRequirement(); ClangRefactorConsumer Consumer; bool HasFailed = false; @@ -499,16 +507,7 @@ auto InvokeRule = [&](RefactoringResultConsumer &Consumer) { logInvocation(Subcommand, Context); - for (RefactoringActionRule *Rule : MatchingRules) { - if (!Rule->hasSelectionRequirement()) - continue; - Rule->invoke(Consumer, Context); - return; - } - // FIXME (Alex L): If more than one initiation succeeded, then the - // rules are ambiguous. - llvm_unreachable( - "The action must have at least one selection rule"); + MatchingRule->invoke(Consumer, Context); }; std::unique_ptr CustomConsumer; @@ -530,7 +529,8 @@ ActiveConsumer.endTU(); return; } - // FIXME (Alex L): Implement non-selection based invocation path. + + InvokeRule(ActiveConsumer); ActiveConsumer.endTU(); })) return true; @@ -567,8 +567,6 @@ } RefactoringActionSubcommand &ActionCommand = **It; - if (ActionCommand.parseArguments()) - return 1; if (Tool.invokeAction(ActionCommand, Options.getCompilations(), Options.getSourcePathList())) return 1;