Index: include/clang/Tooling/Refactoring/RefactoringActionRule.h =================================================================== --- include/clang/Tooling/Refactoring/RefactoringActionRule.h +++ include/clang/Tooling/Refactoring/RefactoringActionRule.h @@ -12,6 +12,7 @@ #include "clang/Basic/LLVM.h" #include "clang/Tooling/Refactoring/AtomicChange.h" +#include "clang/Tooling/Refactoring/Rename/SymbolOccurrences.h" #include "llvm/Support/Error.h" #include @@ -23,7 +24,10 @@ /// A common refactoring action rule interface. class RefactoringActionRule { public: - enum RuleKind { SourceChangeRefactoringRuleKind }; + enum RuleKind { + SourceChangeRefactoringRuleKind, + FindSymbolOccurrencesRefactoringRuleKind + }; RuleKind getRuleKind() const { return Kind; } @@ -59,6 +63,30 @@ } }; +/// An interactive refactoring action that produces symbol occurrences. The +/// consumer is able to let the user decide which occurrences should be used +/// and how the replacements shall be constructed when performing actions +/// like rename. +class FindSymbolOccurrencesRefactoringRule : public RefactoringActionRule { +public: + FindSymbolOccurrencesRefactoringRule() + : RefactoringActionRule(FindSymbolOccurrencesRefactoringRuleKind) {} + + /// Initiates and performs an interactive refactoring action that searches + /// for symbol occurrences. + /// + /// The specific rule must return an llvm::Error with a DiagnosticError + /// payload or None when the refactoring action couldn't be initiated/ + /// performed, or \c SymbolOccurrences when the action was performed + /// successfully. + virtual Expected> + findSymbolOccurrences(RefactoringRuleContext &Context) = 0; + + static bool classof(const RefactoringActionRule *Rule) { + return Rule->getRuleKind() == FindSymbolOccurrencesRefactoringRuleKind; + } +}; + /// A set of refactoring action rules that should have unique initiation /// requirements. using RefactoringActionRules = Index: include/clang/Tooling/Refactoring/RefactoringActionRulesInternal.h =================================================================== --- include/clang/Tooling/Refactoring/RefactoringActionRulesInternal.h +++ include/clang/Tooling/Refactoring/RefactoringActionRulesInternal.h @@ -26,18 +26,26 @@ /// 'perform' method from the specific refactoring method. template struct SpecificRefactoringRuleAdapter {}; -template <> -class SpecificRefactoringRuleAdapter - : public SourceChangeRefactoringRule { -public: - virtual Expected> - perform(RefactoringRuleContext &Context) = 0; - - Expected> - createSourceReplacements(RefactoringRuleContext &Context) final override { - return perform(Context); - } -}; +#define CLANG_REFACTOR_DEFINE_RULE_ADAPTER(ResultType, RuleType, OverrideName) \ + template <> \ + class SpecificRefactoringRuleAdapter : public RuleType { \ + public: \ + virtual Expected> \ + perform(RefactoringRuleContext &Context) = 0; \ + \ + Expected> \ + OverrideName(RefactoringRuleContext &Context) final override { \ + return perform(Context); \ + } \ + }; + +CLANG_REFACTOR_DEFINE_RULE_ADAPTER(AtomicChanges, SourceChangeRefactoringRule, + createSourceReplacements) +CLANG_REFACTOR_DEFINE_RULE_ADAPTER(SymbolOccurrences, + FindSymbolOccurrencesRefactoringRule, + findSymbolOccurrences) + +#undef CLANG_REFACTOR_DEFINE_RULE_ADAPTER /// A specialized refactoring action rule that calls the stored function once /// all the of the requirements are fullfilled. The values produced during the Index: unittests/Tooling/RefactoringActionRulesTest.cpp =================================================================== --- unittests/Tooling/RefactoringActionRulesTest.cpp +++ unittests/Tooling/RefactoringActionRulesTest.cpp @@ -11,6 +11,7 @@ #include "RewriterTestContext.h" #include "clang/Tooling/Refactoring.h" #include "clang/Tooling/Refactoring/RefactoringActionRules.h" +#include "clang/Tooling/Refactoring/Rename/SymbolName.h" #include "clang/Tooling/Tooling.h" #include "llvm/Support/Errc.h" #include "gtest/gtest.h" @@ -162,4 +163,41 @@ EXPECT_EQ(Message, "bad selection"); } +Expected> +findOccurrences(const std::unique_ptr &Rule, + RefactoringRuleContext &Context) { + return cast(*Rule) + .findSymbolOccurrences(Context); +} + +TEST_F(RefactoringActionRulesTest, ReturnSymbolOccurrences) { + auto Rule = createRefactoringRule( + [](selection::SourceSelectionRange Selection) + -> Expected { + SymbolOccurrences Occurrences; + Occurrences.push_back(SymbolOccurrence( + SymbolName("test"), SymbolOccurrence::MatchingSymbol, + Selection.getRange().getBegin())); + return Occurrences; + }, + requiredSelection( + selection::identity())); + + RefactoringRuleContext RefContext(Context.Sources); + SourceLocation Cursor = + Context.Sources.getLocForStartOfFile(Context.Sources.getMainFileID()); + RefContext.setSelectionRange({Cursor, Cursor}); + Expected> Result = + findOccurrences(Rule, RefContext); + + ASSERT_FALSE(!Result); + ASSERT_FALSE(!*Result); + SymbolOccurrences Occurrences = std::move(**Result); + EXPECT_EQ(Occurrences.size(), 1u); + EXPECT_EQ(Occurrences[0].getKind(), SymbolOccurrence::MatchingSymbol); + EXPECT_EQ(Occurrences[0].getNameRanges().size(), 1u); + EXPECT_EQ(Occurrences[0].getNameRanges()[0], + SourceRange(Cursor, Cursor.getLocWithOffset(strlen("test")))); +} + } // end anonymous namespace