aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAlex Lorenz2017-10-02 13:42:43 -0500
committerAlex Lorenz2017-10-02 13:42:43 -0500
commit7b93fc9054c185272b5e4b3c4bc9cd8f512fd9f4 (patch)
tree0477a6207fc1e8b415fd99b1d4837874017db4e7
parentc97363b2c14a50be033ae6af4e360114a234e752 (diff)
downloadclang-7b93fc9054c185272b5e4b3c4bc9cd8f512fd9f4.tar.gz
clang-7b93fc9054c185272b5e4b3c4bc9cd8f512fd9f4.tar.xz
clang-7b93fc9054c185272b5e4b3c4bc9cd8f512fd9f4.zip
[refactor] Simplify the refactoring interface
This commit simplifies the interface for the refactoring action rules and the refactoring requirements. It merges the selection constraints and the selection requirements into one class. The refactoring actions rules must now be implemented using subclassing instead of raw function / lambda pointers. This change also removes a bunch of template-based traits and other template definitions that are now redundant. Differential Revision: https://reviews.llvm.org/D37681 git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@314704 91177308-0d34-0410-b5e6-96231b3b80d8
-rw-r--r--include/clang/Tooling/Refactoring/RefactoringActionRule.h20
-rw-r--r--include/clang/Tooling/Refactoring/RefactoringActionRuleRequirements.h71
-rw-r--r--include/clang/Tooling/Refactoring/RefactoringActionRuleRequirementsInternal.h104
-rw-r--r--include/clang/Tooling/Refactoring/RefactoringActionRules.h114
-rw-r--r--include/clang/Tooling/Refactoring/RefactoringActionRulesInternal.h178
-rw-r--r--include/clang/Tooling/Refactoring/RefactoringResultConsumer.h23
-rw-r--r--include/clang/Tooling/Refactoring/SourceSelectionConstraints.h114
-rw-r--r--lib/Tooling/Refactoring/CMakeLists.txt1
-rw-r--r--lib/Tooling/Refactoring/Rename/RenamingAction.cpp90
-rw-r--r--lib/Tooling/Refactoring/SourceSelectionConstraints.cpp23
-rw-r--r--unittests/Tooling/RefactoringActionRulesTest.cpp129
11 files changed, 308 insertions, 559 deletions
diff --git a/include/clang/Tooling/Refactoring/RefactoringActionRule.h b/include/clang/Tooling/Refactoring/RefactoringActionRule.h
index 1eb6342dfb..c72b37c91d 100644
--- a/include/clang/Tooling/Refactoring/RefactoringActionRule.h
+++ b/include/clang/Tooling/Refactoring/RefactoringActionRule.h
@@ -19,10 +19,12 @@ namespace tooling {
19class RefactoringResultConsumer; 19class RefactoringResultConsumer;
20class RefactoringRuleContext; 20class RefactoringRuleContext;
21 21
22/// A common refactoring action rule interface. 22/// A common refactoring action rule interface that defines the 'invoke'
23class RefactoringActionRule { 23/// function that performs the refactoring operation (either fully or
24/// partially).
25class RefactoringActionRuleBase {
24public: 26public:
25 virtual ~RefactoringActionRule() {} 27 virtual ~RefactoringActionRuleBase() {}
26 28
27 /// Initiates and performs a specific refactoring action. 29 /// Initiates and performs a specific refactoring action.
28 /// 30 ///
@@ -30,17 +32,19 @@ public:
30 /// consumer to propagate the result of the refactoring action. 32 /// consumer to propagate the result of the refactoring action.
31 virtual void invoke(RefactoringResultConsumer &Consumer, 33 virtual void invoke(RefactoringResultConsumer &Consumer,
32 RefactoringRuleContext &Context) = 0; 34 RefactoringRuleContext &Context) = 0;
35};
33 36
37/// A refactoring action rule is a wrapper class around a specific refactoring
38/// action rule (SourceChangeRefactoringRule, etc) that, in addition to invoking
39/// the action, describes the requirements that determine when the action can be
40/// initiated.
41class RefactoringActionRule : public RefactoringActionRuleBase {
42public:
34 /// Returns true when the rule has a source selection requirement that has 43 /// Returns true when the rule has a source selection requirement that has
35 /// to be fullfilled before refactoring can be performed. 44 /// to be fullfilled before refactoring can be performed.
36 virtual bool hasSelectionRequirement() = 0; 45 virtual bool hasSelectionRequirement() = 0;
37}; 46};
38 47
39/// A set of refactoring action rules that should have unique initiation
40/// requirements.
41using RefactoringActionRules =
42 std::vector<std::unique_ptr<RefactoringActionRule>>;
43
44} // end namespace tooling 48} // end namespace tooling
45} // end namespace clang 49} // end namespace clang
46 50
diff --git a/include/clang/Tooling/Refactoring/RefactoringActionRuleRequirements.h b/include/clang/Tooling/Refactoring/RefactoringActionRuleRequirements.h
index 4435a9f865..ebfeeda589 100644
--- a/include/clang/Tooling/Refactoring/RefactoringActionRuleRequirements.h
+++ b/include/clang/Tooling/Refactoring/RefactoringActionRuleRequirements.h
@@ -10,48 +10,49 @@
10#ifndef LLVM_CLANG_TOOLING_REFACTOR_REFACTORING_ACTION_RULE_REQUIREMENTS_H 10#ifndef LLVM_CLANG_TOOLING_REFACTOR_REFACTORING_ACTION_RULE_REQUIREMENTS_H
11#define LLVM_CLANG_TOOLING_REFACTOR_REFACTORING_ACTION_RULE_REQUIREMENTS_H 11#define LLVM_CLANG_TOOLING_REFACTOR_REFACTORING_ACTION_RULE_REQUIREMENTS_H
12 12
13#include "clang/Tooling/Refactoring/RefactoringActionRuleRequirementsInternal.h" 13#include "clang/Basic/LLVM.h"
14#include "clang/Tooling/Refactoring/RefactoringRuleContext.h"
14#include "llvm/Support/Error.h" 15#include "llvm/Support/Error.h"
15#include <type_traits> 16#include <type_traits>
16 17
17namespace clang { 18namespace clang {
18namespace tooling { 19namespace tooling {
19namespace refactoring_action_rules {
20 20
21/// Creates a selection requirement from the given requirement. 21/// A refactoring action rule requirement determines when a refactoring action
22/// rule can be invoked. The rule can be invoked only when all of the
23/// requirements are satisfied.
22/// 24///
23/// Requirements must subclass \c selection::Requirement and implement 25/// Subclasses must implement the
24/// evaluateSelection member function. 26/// 'Expected<T> evaluate(RefactoringRuleContext &) const' member function.
25template <typename T> 27/// \c T is used to determine the return type that is passed to the
26internal::SourceSelectionRequirement< 28/// refactoring rule's constructor.
27 typename selection::internal::EvaluateSelectionChecker< 29/// For example, the \c SourceRangeSelectionRequirement subclass defines
28 decltype(&T::evaluateSelection)>::ArgType, 30/// 'Expected<SourceRange> evaluate(RefactoringRuleContext &Context) const'
29 typename selection::internal::EvaluateSelectionChecker< 31/// function. When this function returns a non-error value, the resulting
30 decltype(&T::evaluateSelection)>::ReturnType, 32/// source range is passed to the specific refactoring action rule
31 T> 33/// constructor (provided all other requirements are satisfied).
32requiredSelection( 34class RefactoringActionRuleRequirement {
33 const T &Requirement, 35 // Expected<T> evaluate(RefactoringRuleContext &Context) const;
34 typename std::enable_if<selection::traits::IsRequirement<T>::value>::type 36};
35 * = nullptr) { 37
36 return internal::SourceSelectionRequirement< 38/// A base class for any requirement that expects some part of the source to be
37 typename selection::internal::EvaluateSelectionChecker<decltype( 39/// selected in an editor (or the refactoring tool with the -selection option).
38 &T::evaluateSelection)>::ArgType, 40class SourceSelectionRequirement : public RefactoringActionRuleRequirement {};
39 typename selection::internal::EvaluateSelectionChecker<decltype( 41
40 &T::evaluateSelection)>::ReturnType, 42/// A selection requirement that is satisfied when any portion of the source
41 T>(Requirement); 43/// text is selected.
42} 44class SourceRangeSelectionRequirement : public SourceSelectionRequirement {
43 45public:
44template <typename T> 46 Expected<SourceRange> evaluate(RefactoringRuleContext &Context) const {
45void requiredSelection( 47 if (Context.getSelectionRange().isValid())
46 const T &, 48 return Context.getSelectionRange();
47 typename std::enable_if< 49 // FIXME: Use a diagnostic.
48 !std::is_base_of<selection::Requirement, T>::value>::type * = nullptr) { 50 return llvm::make_error<llvm::StringError>(
49 static_assert( 51 "refactoring action can't be initiated without a selection",
50 sizeof(T) && false, 52 llvm::inconvertibleErrorCode());
51 "selection requirement must be a class derived from Requirement"); 53 }
52} 54};
53 55
54} // end namespace refactoring_action_rules
55} // end namespace tooling 56} // end namespace tooling
56} // end namespace clang 57} // end namespace clang
57 58
diff --git a/include/clang/Tooling/Refactoring/RefactoringActionRuleRequirementsInternal.h b/include/clang/Tooling/Refactoring/RefactoringActionRuleRequirementsInternal.h
deleted file mode 100644
index bbad23f8fb..0000000000
--- a/include/clang/Tooling/Refactoring/RefactoringActionRuleRequirementsInternal.h
+++ /dev/null
@@ -1,104 +0,0 @@
1//===--- RefactoringActionRuleRequirementsInternal.h - --------------------===//
2//
3// The LLVM Compiler Infrastructure
4//
5// This file is distributed under the University of Illinois Open Source
6// License. See LICENSE.TXT for details.
7//
8//===----------------------------------------------------------------------===//
9
10#ifndef LLVM_CLANG_TOOLING_REFACTOR_REFACTORING_ACTION_REQUIREMENTS_INTERNAL_H
11#define LLVM_CLANG_TOOLING_REFACTOR_REFACTORING_ACTION_REQUIREMENTS_INTERNAL_H
12
13#include "clang/Basic/LLVM.h"
14#include "clang/Tooling/Refactoring/RefactoringRuleContext.h"
15#include "clang/Tooling/Refactoring/SourceSelectionConstraints.h"
16#include <type_traits>
17
18namespace clang {
19namespace tooling {
20namespace refactoring_action_rules {
21namespace internal {
22
23/// A base class for any requirement. Used by the \c IsRequirement trait to
24/// determine if a class is a valid requirement.
25struct RequirementBase {};
26
27/// Defines a type alias of type \T when given \c Expected<Optional<T>>, or
28/// \c T otherwise.
29template <typename T> struct DropExpectedOptional { using Type = T; };
30
31template <typename T> struct DropExpectedOptional<Expected<Optional<T>>> {
32 using Type = T;
33};
34
35/// The \c requiredSelection refactoring action requirement is represented
36/// using this type.
37template <typename InputT, typename OutputT, typename RequirementT>
38struct SourceSelectionRequirement
39 : std::enable_if<selection::traits::IsConstraint<InputT>::value &&
40 selection::traits::IsRequirement<RequirementT>::value,
41 RequirementBase>::type {
42 using OutputType = typename DropExpectedOptional<OutputT>::Type;
43
44 SourceSelectionRequirement(const RequirementT &Requirement)
45 : Requirement(Requirement) {}
46
47 /// Evaluates the action rule requirement by ensuring that both the selection
48 /// constraint and the selection requirement can be evaluated with the given
49 /// context.
50 ///
51 /// \returns None if the selection constraint is not evaluated successfully,
52 /// Error if the selection requirement is not evaluated successfully or
53 /// an OutputT if the selection requirement was successfully. The OutpuT
54 /// value is wrapped in Expected<Optional<>> which is then unwrapped by the
55 /// refactoring action rule before passing the value to the refactoring
56 /// function.
57 Expected<Optional<OutputType>> evaluate(RefactoringRuleContext &Context) {
58 Optional<InputT> Value = InputT::evaluate(Context);
59 if (!Value)
60 return None;
61 return std::move(Requirement.evaluateSelection(Context, *Value));
62 }
63
64private:
65 const RequirementT Requirement;
66};
67
68} // end namespace internal
69
70namespace traits {
71
72/// A type trait that returns true iff the given type is a valid rule
73/// requirement.
74template <typename First, typename... Rest>
75struct IsRequirement : std::conditional<IsRequirement<First>::value &&
76 IsRequirement<Rest...>::value,
77 std::true_type, std::false_type>::type {
78};
79
80template <typename T>
81struct IsRequirement<T>
82 : std::conditional<std::is_base_of<internal::RequirementBase, T>::value,
83 std::true_type, std::false_type>::type {};
84
85/// A type trait that returns true when the given type has at least one source
86/// selection requirement.
87template <typename First, typename... Rest>
88struct HasSelectionRequirement
89 : std::conditional<HasSelectionRequirement<First>::value ||
90 HasSelectionRequirement<Rest...>::value,
91 std::true_type, std::false_type>::type {};
92
93template <typename I, typename O, typename R>
94struct HasSelectionRequirement<internal::SourceSelectionRequirement<I, O, R>>
95 : std::true_type {};
96
97template <typename T> struct HasSelectionRequirement<T> : std::false_type {};
98
99} // end namespace traits
100} // end namespace refactoring_action_rules
101} // end namespace tooling
102} // end namespace clang
103
104#endif // LLVM_CLANG_TOOLING_REFACTOR_REFACTORING_ACTION_REQUIREMENTS_INTERNAL_H
diff --git a/include/clang/Tooling/Refactoring/RefactoringActionRules.h b/include/clang/Tooling/Refactoring/RefactoringActionRules.h
index 1ae9953485..33206d9a5d 100644
--- a/include/clang/Tooling/Refactoring/RefactoringActionRules.h
+++ b/include/clang/Tooling/Refactoring/RefactoringActionRules.h
@@ -16,60 +16,78 @@
16namespace clang { 16namespace clang {
17namespace tooling { 17namespace tooling {
18 18
19class RefactoringRuleContext; 19/// Creates a new refactoring action rule that constructs and invokes the
20 20/// \c RuleType rule when all of the requirements are satisfied.
21namespace refactoring_action_rules {
22
23/// Creates a new refactoring action rule that invokes the given function once
24/// all of the requirements are satisfied. The values produced during the
25/// evaluation of requirements are passed to the given function (in the order of
26/// requirements).
27/// 21///
28/// \param RefactoringFunction the function that will perform the refactoring 22/// This function takes in a list of values whose type derives from
29/// once the requirements are satisfied. The function must return a valid 23/// \c RefactoringActionRuleRequirement. These values describe the initiation
30/// refactoring result type wrapped in an \c Expected type. The following result 24/// requirements that have to be satisfied by the refactoring engine before
31/// types are currently supported: 25/// the provided action rule can be constructed and invoked. The engine
26/// verifies that the requirements are satisfied by evaluating them (using the
27/// 'evaluate' member function) and checking that the results don't contain
28/// any errors. Once all requirements are satisfied, the provided refactoring
29/// rule is constructed by passing in the values returned by the requirements'
30/// evaluate functions as arguments to the constructor. The rule is then invoked
31/// immediately after construction.
32/// 32///
33/// - AtomicChanges: the refactoring function will be used to create source 33/// The separation of requirements, their evaluation and the invocation of the
34/// replacements. 34/// refactoring action rule allows the refactoring clients to:
35/// - Disable refactoring action rules whose requirements are not supported.
36/// - Gather the set of options and define a command-line / visual interface
37/// that allows users to input these options without ever invoking the
38/// action.
39template <typename RuleType, typename... RequirementTypes>
40std::unique_ptr<RefactoringActionRule>
41createRefactoringActionRule(const RequirementTypes &... Requirements);
42
43/// A set of refactoring action rules that should have unique initiation
44/// requirements.
45using RefactoringActionRules =
46 std::vector<std::unique_ptr<RefactoringActionRule>>;
47
48/// A type of refactoring action rule that produces source replacements in the
49/// form of atomic changes.
35/// 50///
36/// \param Requirements a set of rule requirements that have to be satisfied. 51/// This action rule is typically used for local refactorings that replace
37/// Each requirement must be a valid requirement, i.e. the value of 52/// source in a single AST unit.
38/// \c traits::IsRequirement<T> must be true. The following requirements are 53class SourceChangeRefactoringRule : public RefactoringActionRuleBase {
39/// currently supported: 54public:
55 void invoke(RefactoringResultConsumer &Consumer,
56 RefactoringRuleContext &Context) final override {
57 Expected<AtomicChanges> Changes = createSourceReplacements(Context);
58 if (!Changes)
59 Consumer.handleError(Changes.takeError());
60 else
61 Consumer.handle(std::move(*Changes));
62 }
63
64private:
65 virtual Expected<AtomicChanges>
66 createSourceReplacements(RefactoringRuleContext &Context) = 0;
67};
68
69/// A type of refactoring action rule that finds a set of symbol occurrences
70/// that reference a particular symbol.
40/// 71///
41/// - requiredSelection: The refactoring function won't be invoked unless the 72/// This action rule is typically used for an interactive rename that allows
42/// given selection requirement is satisfied. 73/// users to specify the new name and the set of selected occurrences during
43template <typename ResultType, typename... RequirementTypes> 74/// the refactoring.
44std::unique_ptr<RefactoringActionRule> 75class FindSymbolOccurrencesRefactoringRule : public RefactoringActionRuleBase {
45createRefactoringRule(Expected<ResultType> (*RefactoringFunction)( 76public:
46 const RefactoringRuleContext &, 77 void invoke(RefactoringResultConsumer &Consumer,
47 typename RequirementTypes::OutputType...), 78 RefactoringRuleContext &Context) final override {
48 const RequirementTypes &... Requirements) { 79 Expected<SymbolOccurrences> Occurrences = findSymbolOccurrences(Context);
49 static_assert(tooling::traits::IsValidRefactoringResult<ResultType>::value, 80 if (!Occurrences)
50 "invalid refactoring result type"); 81 Consumer.handleError(Occurrences.takeError());
51 static_assert(traits::IsRequirement<RequirementTypes...>::value, 82 else
52 "invalid refactoring action rule requirement"); 83 Consumer.handle(std::move(*Occurrences));
53 return llvm::make_unique<internal::PlainFunctionRule< 84 }
54 decltype(RefactoringFunction), RequirementTypes...>>(
55 RefactoringFunction, std::make_tuple(Requirements...));
56}
57 85
58template < 86private:
59 typename Callable, typename... RequirementTypes, 87 virtual Expected<SymbolOccurrences>
60 typename Fn = decltype(&Callable::operator()), 88 findSymbolOccurrences(RefactoringRuleContext &Context) = 0;
61 typename ResultType = typename internal::LambdaDeducer<Fn>::ReturnType, 89};
62 bool IsNonCapturingLambda = std::is_convertible<
63 Callable, typename internal::LambdaDeducer<Fn>::FunctionType>::value,
64 typename = typename std::enable_if<IsNonCapturingLambda>::type>
65std::unique_ptr<RefactoringActionRule>
66createRefactoringRule(const Callable &C,
67 const RequirementTypes &... Requirements) {
68 typename internal::LambdaDeducer<Fn>::FunctionType Func = C;
69 return createRefactoringRule(Func, Requirements...);
70}
71 90
72} // end namespace refactoring_action_rules
73} // end namespace tooling 91} // end namespace tooling
74} // end namespace clang 92} // end namespace clang
75 93
diff --git a/include/clang/Tooling/Refactoring/RefactoringActionRulesInternal.h b/include/clang/Tooling/Refactoring/RefactoringActionRulesInternal.h
index e26b92dd64..61db7400ac 100644
--- a/include/clang/Tooling/Refactoring/RefactoringActionRulesInternal.h
+++ b/include/clang/Tooling/Refactoring/RefactoringActionRulesInternal.h
@@ -20,108 +20,90 @@
20 20
21namespace clang { 21namespace clang {
22namespace tooling { 22namespace tooling {
23namespace refactoring_action_rules {
24namespace internal { 23namespace internal {
25 24
26/// A specialized refactoring action rule that calls the stored function once 25inline llvm::Error findError() { return llvm::Error::success(); }
27/// all the of the requirements are fullfilled. The values produced during the 26
28/// evaluation of requirements are passed to the stored function. 27/// Scans the tuple and returns a valid \c Error if any of the values are
29template <typename FunctionType, typename... RequirementTypes> 28/// invalid.
30class PlainFunctionRule final : public RefactoringActionRule { 29template <typename FirstT, typename... RestT>
31public: 30llvm::Error findError(Expected<FirstT> &First, Expected<RestT> &... Rest) {
32 PlainFunctionRule(FunctionType Function, 31 if (!First)
33 std::tuple<RequirementTypes...> &&Requirements) 32 return First.takeError();
34 : Function(Function), Requirements(std::move(Requirements)) {} 33 return findError(Rest...);
35 34}
36 void invoke(RefactoringResultConsumer &Consumer, 35
37 RefactoringRuleContext &Context) override { 36template <typename RuleType, typename... RequirementTypes, size_t... Is>
38 return invokeImpl(Consumer, Context, 37void invokeRuleAfterValidatingRequirements(
39 llvm::index_sequence_for<RequirementTypes...>()); 38 RefactoringResultConsumer &Consumer, RefactoringRuleContext &Context,
40 } 39 const std::tuple<RequirementTypes...> &Requirements,
41 40 llvm::index_sequence<Is...>) {
42 bool hasSelectionRequirement() override { 41 // Check if the requirements we're interested in can be evaluated.
43 return traits::HasSelectionRequirement<RequirementTypes...>::value; 42 auto Values =
44 } 43 std::make_tuple(std::get<Is>(Requirements).evaluate(Context)...);
45 44 auto Err = findError(std::get<Is>(Values)...);
46private: 45 if (Err)
47 /// Returns \c T when given \c Expected<Optional<T>>, or \c T otherwise. 46 return Consumer.handleError(std::move(Err));
48 template <typename T> 47 // Construct the target action rule by extracting the evaluated
49 static T &&unwrapRequirementResult(Expected<Optional<T>> &&X) { 48 // requirements from Expected<> wrappers and then run it.
50 assert(X && "unexpected diagnostic!"); 49 RuleType((*std::get<Is>(Values))...).invoke(Consumer, Context);
51 return std::move(**X); 50}
52 } 51
53 template <typename T> static T &&unwrapRequirementResult(T &&X) { 52/// A type trait that returns true when the given type list has at least one
54 return std::move(X); 53/// type whose base is the given base type.
55 } 54template <typename Base, typename First, typename... Rest>
56 55struct HasBaseOf : std::conditional<HasBaseOf<Base, First>::value ||
57 /// Scans the tuple and returns a \c PartialDiagnosticAt 56 HasBaseOf<Base, Rest...>::value,
58 /// from the first invalid \c DiagnosticOr value. Returns \c None if all 57 std::true_type, std::false_type>::type {};
59 /// values are valid. 58
60 template <typename FirstT, typename... RestT> 59template <typename Base, typename T>
61 static Optional<llvm::Error> findErrorNone(FirstT &First, RestT &... Rest) { 60struct HasBaseOf<Base, T> : std::is_base_of<Base, T> {};
62 Optional<llvm::Error> Result = takeErrorNone(First); 61
63 if (Result) 62/// A type trait that returns true when the given type list contains types that
64 return Result; 63/// derive from Base.
65 return findErrorNone(Rest...); 64template <typename Base, typename First, typename... Rest>
66 } 65struct AreBaseOf : std::conditional<AreBaseOf<Base, First>::value &&
67 66 AreBaseOf<Base, Rest...>::value,
68 static Optional<llvm::Error> findErrorNone() { return None; } 67 std::true_type, std::false_type>::type {};
69 68
70 template <typename T> static Optional<llvm::Error> takeErrorNone(T &) { 69template <typename Base, typename T>
71 return None; 70struct AreBaseOf<Base, T> : std::is_base_of<Base, T> {};
72 }
73
74 template <typename T>
75 static Optional<llvm::Error> takeErrorNone(Expected<Optional<T>> &Diag) {
76 if (!Diag)
77 return std::move(Diag.takeError());
78 if (!*Diag)
79 return llvm::Error::success(); // Initiation failed without a diagnostic.
80 return None;
81 }
82
83 template <size_t... Is>
84 void invokeImpl(RefactoringResultConsumer &Consumer,
85 RefactoringRuleContext &Context,
86 llvm::index_sequence<Is...> Seq) {
87 // Initiate the operation.
88 auto Values =
89 std::make_tuple(std::get<Is>(Requirements).evaluate(Context)...);
90 Optional<llvm::Error> InitiationFailure =
91 findErrorNone(std::get<Is>(Values)...);
92 if (InitiationFailure) {
93 llvm::Error Error = std::move(*InitiationFailure);
94 if (!Error)
95 // FIXME: Use a diagnostic.
96 return Consumer.handleError(llvm::make_error<llvm::StringError>(
97 "refactoring action can't be initiated with the specified "
98 "selection range",
99 llvm::inconvertibleErrorCode()));
100 return Consumer.handleError(std::move(Error));
101 }
102 // Perform the operation.
103 auto Result = Function(
104 Context, unwrapRequirementResult(std::move(std::get<Is>(Values)))...);
105 if (!Result)
106 return Consumer.handleError(Result.takeError());
107 Consumer.handle(std::move(*Result));
108 }
109
110 FunctionType Function;
111 std::tuple<RequirementTypes...> Requirements;
112};
113
114/// Used to deduce the refactoring result type for the lambda that passed into
115/// createRefactoringRule.
116template <typename T> struct LambdaDeducer;
117template <typename T, typename R, typename... Args>
118struct LambdaDeducer<R (T::*)(const RefactoringRuleContext &, Args...) const> {
119 using ReturnType = R;
120 using FunctionType = R (*)(const RefactoringRuleContext &, Args...);
121};
122 71
123} // end namespace internal 72} // end namespace internal
124} // end namespace refactoring_action_rules 73
74template <typename RuleType, typename... RequirementTypes>
75std::unique_ptr<RefactoringActionRule>
76createRefactoringActionRule(const RequirementTypes &... Requirements) {
77 static_assert(std::is_base_of<RefactoringActionRuleBase, RuleType>::value,
78 "Expected a refactoring action rule type");
79 static_assert(internal::AreBaseOf<RefactoringActionRuleRequirement,
80 RequirementTypes...>::value,
81 "Expected a list of refactoring action rules");
82
83 class Rule final : public RefactoringActionRule {
84 public:
85 Rule(std::tuple<RequirementTypes...> Requirements)
86 : Requirements(Requirements) {}
87
88 void invoke(RefactoringResultConsumer &Consumer,
89 RefactoringRuleContext &Context) override {
90 internal::invokeRuleAfterValidatingRequirements<RuleType>(
91 Consumer, Context, Requirements,
92 llvm::index_sequence_for<RequirementTypes...>());
93 }
94
95 bool hasSelectionRequirement() override {
96 return internal::HasBaseOf<SourceSelectionRequirement,
97 RequirementTypes...>::value;
98 }
99
100 private:
101 std::tuple<RequirementTypes...> Requirements;
102 };
103
104 return llvm::make_unique<Rule>(std::make_tuple(Requirements...));
105}
106
125} // end namespace tooling 107} // end namespace tooling
126} // end namespace clang 108} // end namespace clang
127 109
diff --git a/include/clang/Tooling/Refactoring/RefactoringResultConsumer.h b/include/clang/Tooling/Refactoring/RefactoringResultConsumer.h
index 391c4d8b6d..fe7738e734 100644
--- a/include/clang/Tooling/Refactoring/RefactoringResultConsumer.h
+++ b/include/clang/Tooling/Refactoring/RefactoringResultConsumer.h
@@ -46,29 +46,6 @@ private:
46 } 46 }
47}; 47};
48 48
49namespace traits {
50namespace internal {
51
52template <typename T> struct HasHandle {
53private:
54 template <typename ClassT>
55 static auto check(ClassT *) -> typename std::is_same<
56 decltype(std::declval<ClassT>().handle(std::declval<T>())), void>::type;
57
58 template <typename> static std::false_type check(...);
59
60public:
61 using Type = decltype(check<RefactoringResultConsumer>(nullptr));
62};
63
64} // end namespace internal
65
66/// A type trait that returns true iff the given type is a valid refactoring
67/// result.
68template <typename T>
69struct IsValidRefactoringResult : internal::HasHandle<T>::Type {};
70
71} // end namespace traits
72} // end namespace tooling 49} // end namespace tooling
73} // end namespace clang 50} // end namespace clang
74 51
diff --git a/include/clang/Tooling/Refactoring/SourceSelectionConstraints.h b/include/clang/Tooling/Refactoring/SourceSelectionConstraints.h
deleted file mode 100644
index 7369ca0408..0000000000
--- a/include/clang/Tooling/Refactoring/SourceSelectionConstraints.h
+++ /dev/null
@@ -1,114 +0,0 @@
1//===--- SourceSelectionConstraints.h - Clang refactoring library ---------===//
2//
3// The LLVM Compiler Infrastructure
4//
5// This file is distributed under the University of Illinois Open Source
6// License. See LICENSE.TXT for details.
7//
8//===----------------------------------------------------------------------===//
9
10#ifndef LLVM_CLANG_TOOLING_REFACTOR_SOURCE_SELECTION_CONSTRAINTS_H
11#define LLVM_CLANG_TOOLING_REFACTOR_SOURCE_SELECTION_CONSTRAINTS_H
12
13#include "clang/Basic/LLVM.h"
14#include "clang/Basic/SourceLocation.h"
15#include "clang/Tooling/Refactoring/RefactoringRuleContext.h"
16#include <type_traits>
17
18namespace clang {
19namespace tooling {
20
21class RefactoringRuleContext;
22
23namespace selection {
24
25/// This constraint is satisfied when any portion of the source text is
26/// selected. It can be used to obtain the raw source selection range.
27struct SourceSelectionRange {
28 SourceSelectionRange(const SourceManager &SM, SourceRange Range)
29 : SM(SM), Range(Range) {}
30
31 const SourceManager &getSources() const { return SM; }
32 SourceRange getRange() const { return Range; }
33
34 static Optional<SourceSelectionRange>
35 evaluate(RefactoringRuleContext &Context);
36
37private:
38 const SourceManager &SM;
39 SourceRange Range;
40};
41
42/// A custom selection requirement.
43class Requirement {
44 /// Subclasses must implement
45 /// 'T evaluateSelection(const RefactoringRuleContext &,
46 /// SelectionConstraint) const' member function. \c T is used to determine
47 /// the return type that is passed to the refactoring rule's function.
48 /// If T is \c DiagnosticOr<S> , then \c S is passed to the rule's function
49 /// using move semantics.
50 /// Otherwise, T is passed to the function directly using move semantics.
51 ///
52 /// The different return type rules allow refactoring actions to fail
53 /// initiation when the relevant portions of AST aren't selected.
54};
55
56namespace traits {
57
58/// A type trait that returns true iff the given type is a valid selection
59/// constraint.
60template <typename T> struct IsConstraint : public std::false_type {};
61
62} // end namespace traits
63
64namespace internal {
65
66template <typename T> struct EvaluateSelectionChecker : std::false_type {};
67
68template <typename T, typename R, typename A>
69struct EvaluateSelectionChecker<R (T::*)(const RefactoringRuleContext &, A)
70 const> : std::true_type {
71 using ReturnType = R;
72 using ArgType = A;
73};
74
75template <typename T> class Identity : public Requirement {
76public:
77 T evaluateSelection(const RefactoringRuleContext &, T Value) const {
78 return std::move(Value);
79 }
80};
81
82} // end namespace internal
83
84/// A identity function that returns the given selection constraint is provided
85/// for convenience, as it can be passed to \c requiredSelection directly.
86template <typename T> internal::Identity<T> identity() {
87 static_assert(
88 traits::IsConstraint<T>::value,
89 "selection::identity can be used with selection constraints only");
90 return internal::Identity<T>();
91}
92
93namespace traits {
94
95template <>
96struct IsConstraint<SourceSelectionRange> : public std::true_type {};
97
98/// A type trait that returns true iff \c T is a valid selection requirement.
99template <typename T>
100struct IsRequirement
101 : std::conditional<
102 std::is_base_of<Requirement, T>::value &&
103 internal::EvaluateSelectionChecker<decltype(
104 &T::evaluateSelection)>::value &&
105 IsConstraint<typename internal::EvaluateSelectionChecker<decltype(
106 &T::evaluateSelection)>::ArgType>::value,
107 std::true_type, std::false_type>::type {};
108
109} // end namespace traits
110} // end namespace selection
111} // end namespace tooling
112} // end namespace clang
113
114#endif // LLVM_CLANG_TOOLING_REFACTOR_SOURCE_SELECTION_CONSTRAINTS_H
diff --git a/lib/Tooling/Refactoring/CMakeLists.txt b/lib/Tooling/Refactoring/CMakeLists.txt
index ff9cd1ff9e..43ea1d9c54 100644
--- a/lib/Tooling/Refactoring/CMakeLists.txt
+++ b/lib/Tooling/Refactoring/CMakeLists.txt
@@ -9,7 +9,6 @@ add_clang_library(clangToolingRefactor
9 Rename/USRFinder.cpp 9 Rename/USRFinder.cpp
10 Rename/USRFindingAction.cpp 10 Rename/USRFindingAction.cpp
11 Rename/USRLocFinder.cpp 11 Rename/USRLocFinder.cpp
12 SourceSelectionConstraints.cpp
13 12
14 LINK_LIBS 13 LINK_LIBS
15 clangAST 14 clangAST
diff --git a/lib/Tooling/Refactoring/Rename/RenamingAction.cpp b/lib/Tooling/Refactoring/Rename/RenamingAction.cpp
index 384e466015..fe3067f825 100644
--- a/lib/Tooling/Refactoring/Rename/RenamingAction.cpp
+++ b/lib/Tooling/Refactoring/Rename/RenamingAction.cpp
@@ -23,7 +23,6 @@
23#include "clang/Tooling/CommonOptionsParser.h" 23#include "clang/Tooling/CommonOptionsParser.h"
24#include "clang/Tooling/Refactoring.h" 24#include "clang/Tooling/Refactoring.h"
25#include "clang/Tooling/Refactoring/RefactoringAction.h" 25#include "clang/Tooling/Refactoring/RefactoringAction.h"
26#include "clang/Tooling/Refactoring/RefactoringActionRules.h"
27#include "clang/Tooling/Refactoring/Rename/USRFinder.h" 26#include "clang/Tooling/Refactoring/Rename/USRFinder.h"
28#include "clang/Tooling/Refactoring/Rename/USRFindingAction.h" 27#include "clang/Tooling/Refactoring/Rename/USRFindingAction.h"
29#include "clang/Tooling/Refactoring/Rename/USRLocFinder.h" 28#include "clang/Tooling/Refactoring/Rename/USRLocFinder.h"
@@ -39,53 +38,78 @@ namespace tooling {
39 38
40namespace { 39namespace {
41 40
42class LocalRename : public RefactoringAction { 41class SymbolSelectionRequirement : public SourceRangeSelectionRequirement {
43public: 42public:
44 StringRef getCommand() const override { return "local-rename"; } 43 Expected<const NamedDecl *> evaluate(RefactoringRuleContext &Context) const {
45 44 Expected<SourceRange> Selection =
46 StringRef getDescription() const override { 45 SourceRangeSelectionRequirement::evaluate(Context);
47 return "Finds and renames symbols in code with no indexer support"; 46 if (!Selection)
47 return Selection.takeError();
48 const NamedDecl *ND =
49 getNamedDeclAt(Context.getASTContext(), Selection->getBegin());
50 if (!ND) {
51 // FIXME: Use a diagnostic.
52 return llvm::make_error<StringError>("no symbol selected",
53 llvm::inconvertibleErrorCode());
54 }
55 return getCanonicalSymbolDeclaration(ND);
48 } 56 }
57};
49 58
50 /// Returns a set of refactoring actions rules that are defined by this 59class OccurrenceFinder final : public FindSymbolOccurrencesRefactoringRule {
51 /// action. 60public:
52 RefactoringActionRules createActionRules() const override { 61 OccurrenceFinder(const NamedDecl *ND) : ND(ND) {}
53 using namespace refactoring_action_rules;
54 RefactoringActionRules Rules;
55 Rules.push_back(createRefactoringRule(
56 renameOccurrences, requiredSelection(SymbolSelectionRequirement())));
57 return Rules;
58 }
59 62
60private: 63 Expected<SymbolOccurrences>
61 static Expected<AtomicChanges> 64 findSymbolOccurrences(RefactoringRuleContext &Context) override {
62 renameOccurrences(const RefactoringRuleContext &Context,
63 const NamedDecl *ND) {
64 std::vector<std::string> USRs = 65 std::vector<std::string> USRs =
65 getUSRsForDeclaration(ND, Context.getASTContext()); 66 getUSRsForDeclaration(ND, Context.getASTContext());
66 std::string PrevName = ND->getNameAsString(); 67 std::string PrevName = ND->getNameAsString();
67 auto Occurrences = getOccurrencesOfUSRs( 68 return getOccurrencesOfUSRs(
68 USRs, PrevName, Context.getASTContext().getTranslationUnitDecl()); 69 USRs, PrevName, Context.getASTContext().getTranslationUnitDecl());
70 }
69 71
72private:
73 const NamedDecl *ND;
74};
75
76class RenameOccurrences final : public SourceChangeRefactoringRule {
77public:
78 RenameOccurrences(const NamedDecl *ND) : Finder(ND) {}
79
80 Expected<AtomicChanges>
81 createSourceReplacements(RefactoringRuleContext &Context) {
82 Expected<SymbolOccurrences> Occurrences =
83 Finder.findSymbolOccurrences(Context);
84 if (!Occurrences)
85 return Occurrences.takeError();
70 // FIXME: This is a temporary workaround that's needed until the refactoring 86 // FIXME: This is a temporary workaround that's needed until the refactoring
71 // options are implemented. 87 // options are implemented.
72 StringRef NewName = "Bar"; 88 StringRef NewName = "Bar";
73 return createRenameReplacements( 89 return createRenameReplacements(
74 Occurrences, Context.getASTContext().getSourceManager(), NewName); 90 *Occurrences, Context.getASTContext().getSourceManager(), NewName);
75 } 91 }
76 92
77 class SymbolSelectionRequirement : public selection::Requirement { 93private:
78 public: 94 OccurrenceFinder Finder;
79 Expected<Optional<const NamedDecl *>> 95};
80 evaluateSelection(const RefactoringRuleContext &Context, 96
81 selection::SourceSelectionRange Selection) const { 97class LocalRename final : public RefactoringAction {
82 const NamedDecl *ND = getNamedDeclAt(Context.getASTContext(), 98public:
83 Selection.getRange().getBegin()); 99 StringRef getCommand() const override { return "local-rename"; }
84 if (!ND) 100
85 return None; 101 StringRef getDescription() const override {
86 return getCanonicalSymbolDeclaration(ND); 102 return "Finds and renames symbols in code with no indexer support";
87 } 103 }
88 }; 104
105 /// Returns a set of refactoring actions rules that are defined by this
106 /// action.
107 RefactoringActionRules createActionRules() const override {
108 RefactoringActionRules Rules;
109 Rules.push_back(createRefactoringActionRule<RenameOccurrences>(
110 SymbolSelectionRequirement()));
111 return Rules;
112 }
89}; 113};
90 114
91} // end anonymous namespace 115} // end anonymous namespace
diff --git a/lib/Tooling/Refactoring/SourceSelectionConstraints.cpp b/lib/Tooling/Refactoring/SourceSelectionConstraints.cpp
deleted file mode 100644
index 5fbe2946a9..0000000000
--- a/lib/Tooling/Refactoring/SourceSelectionConstraints.cpp
+++ /dev/null
@@ -1,23 +0,0 @@
1//===--- SourceSelectionConstraints.cpp - Evaluate selection constraints --===//
2//
3// The LLVM Compiler Infrastructure
4//
5// This file is distributed under the University of Illinois Open Source
6// License. See LICENSE.TXT for details.
7//
8//===----------------------------------------------------------------------===//
9
10#include "clang/Tooling/Refactoring/SourceSelectionConstraints.h"
11#include "clang/Tooling/Refactoring/RefactoringRuleContext.h"
12
13using namespace clang;
14using namespace tooling;
15using namespace selection;
16
17Optional<SourceSelectionRange>
18SourceSelectionRange::evaluate(RefactoringRuleContext &Context) {
19 SourceRange R = Context.getSelectionRange();
20 if (R.isInvalid())
21 return None;
22 return SourceSelectionRange(Context.getSources(), R);
23}
diff --git a/unittests/Tooling/RefactoringActionRulesTest.cpp b/unittests/Tooling/RefactoringActionRulesTest.cpp
index cf91b5fc8f..afab34ef10 100644
--- a/unittests/Tooling/RefactoringActionRulesTest.cpp
+++ b/unittests/Tooling/RefactoringActionRulesTest.cpp
@@ -18,7 +18,6 @@
18 18
19using namespace clang; 19using namespace clang;
20using namespace tooling; 20using namespace tooling;
21using namespace refactoring_action_rules;
22 21
23namespace { 22namespace {
24 23
@@ -56,29 +55,39 @@ createReplacements(const std::unique_ptr<RefactoringActionRule> &Rule,
56} 55}
57 56
58TEST_F(RefactoringActionRulesTest, MyFirstRefactoringRule) { 57TEST_F(RefactoringActionRulesTest, MyFirstRefactoringRule) {
59 auto ReplaceAWithB = 58 class ReplaceAWithB : public SourceChangeRefactoringRule {
60 [](const RefactoringRuleContext &, 59 std::pair<SourceRange, int> Selection;
61 std::pair<selection::SourceSelectionRange, int> Selection) 60
62 -> Expected<AtomicChanges> { 61 public:
63 const SourceManager &SM = Selection.first.getSources(); 62 ReplaceAWithB(std::pair<SourceRange, int> Selection)
64 SourceLocation Loc = Selection.first.getRange().getBegin().getLocWithOffset( 63 : Selection(Selection) {}
65 Selection.second); 64
66 AtomicChange Change(SM, Loc); 65 Expected<AtomicChanges>
67 llvm::Error E = Change.replace(SM, Loc, 1, "b"); 66 createSourceReplacements(RefactoringRuleContext &Context) {
68 if (E) 67 const SourceManager &SM = Context.getSources();
69 return std::move(E); 68 SourceLocation Loc =
70 return AtomicChanges{Change}; 69 Selection.first.getBegin().getLocWithOffset(Selection.second);
70 AtomicChange Change(SM, Loc);
71 llvm::Error E = Change.replace(SM, Loc, 1, "b");
72 if (E)
73 return std::move(E);
74 return AtomicChanges{Change};
75 }
71 }; 76 };
72 class SelectionRequirement : public selection::Requirement { 77
78 class SelectionRequirement : public SourceRangeSelectionRequirement {
73 public: 79 public:
74 std::pair<selection::SourceSelectionRange, int> 80 Expected<std::pair<SourceRange, int>>
75 evaluateSelection(const RefactoringRuleContext &, 81 evaluate(RefactoringRuleContext &Context) const {
76 selection::SourceSelectionRange Selection) const { 82 Expected<SourceRange> R =
77 return std::make_pair(Selection, 20); 83 SourceRangeSelectionRequirement::evaluate(Context);
84 if (!R)
85 return R.takeError();
86 return std::make_pair(*R, 20);
78 } 87 }
79 }; 88 };
80 auto Rule = createRefactoringRule(ReplaceAWithB, 89 auto Rule =
81 requiredSelection(SelectionRequirement())); 90 createRefactoringActionRule<ReplaceAWithB>(SelectionRequirement());
82 91
83 // When the requirements are satisifed, the rule's function must be invoked. 92 // When the requirements are satisifed, the rule's function must be invoked.
84 { 93 {
@@ -123,54 +132,24 @@ TEST_F(RefactoringActionRulesTest, MyFirstRefactoringRule) {
123 llvm::handleAllErrors( 132 llvm::handleAllErrors(
124 ErrorOrResult.takeError(), 133 ErrorOrResult.takeError(),
125 [&](llvm::StringError &Error) { Message = Error.getMessage(); }); 134 [&](llvm::StringError &Error) { Message = Error.getMessage(); });
126 EXPECT_EQ(Message, "refactoring action can't be initiated with the " 135 EXPECT_EQ(Message,
127 "specified selection range"); 136 "refactoring action can't be initiated without a selection");
128 } 137 }
129} 138}
130 139
131TEST_F(RefactoringActionRulesTest, ReturnError) { 140TEST_F(RefactoringActionRulesTest, ReturnError) {
132 Expected<AtomicChanges> (*Func)(const RefactoringRuleContext &, 141 class ErrorRule : public SourceChangeRefactoringRule {
133 selection::SourceSelectionRange) =
134 [](const RefactoringRuleContext &,
135 selection::SourceSelectionRange) -> Expected<AtomicChanges> {
136 return llvm::make_error<llvm::StringError>(
137 "Error", llvm::make_error_code(llvm::errc::invalid_argument));
138 };
139 auto Rule = createRefactoringRule(
140 Func, requiredSelection(
141 selection::identity<selection::SourceSelectionRange>()));
142
143 RefactoringRuleContext RefContext(Context.Sources);
144 SourceLocation Cursor =
145 Context.Sources.getLocForStartOfFile(Context.Sources.getMainFileID());
146 RefContext.setSelectionRange({Cursor, Cursor});
147 Expected<AtomicChanges> Result = createReplacements(Rule, RefContext);
148
149 ASSERT_TRUE(!Result);
150 std::string Message;
151 llvm::handleAllErrors(Result.takeError(), [&](llvm::StringError &Error) {
152 Message = Error.getMessage();
153 });
154 EXPECT_EQ(Message, "Error");
155}
156
157TEST_F(RefactoringActionRulesTest, ReturnInitiationDiagnostic) {
158 RefactoringRuleContext RefContext(Context.Sources);
159 class SelectionRequirement : public selection::Requirement {
160 public: 142 public:
161 Expected<Optional<int>> 143 ErrorRule(SourceRange R) {}
162 evaluateSelection(const RefactoringRuleContext &, 144 Expected<AtomicChanges> createSourceReplacements(RefactoringRuleContext &) {
163 selection::SourceSelectionRange Selection) const {
164 return llvm::make_error<llvm::StringError>( 145 return llvm::make_error<llvm::StringError>(
165 "bad selection", llvm::make_error_code(llvm::errc::invalid_argument)); 146 "Error", llvm::make_error_code(llvm::errc::invalid_argument));
166 } 147 }
167 }; 148 };
168 auto Rule = createRefactoringRule(
169 [](const RefactoringRuleContext &, int) -> Expected<AtomicChanges> {
170 llvm::report_fatal_error("Should not run!");
171 },
172 requiredSelection(SelectionRequirement()));
173 149
150 auto Rule =
151 createRefactoringActionRule<ErrorRule>(SourceRangeSelectionRequirement());
152 RefactoringRuleContext RefContext(Context.Sources);
174 SourceLocation Cursor = 153 SourceLocation Cursor =
175 Context.Sources.getLocForStartOfFile(Context.Sources.getMainFileID()); 154 Context.Sources.getLocForStartOfFile(Context.Sources.getMainFileID());
176 RefContext.setSelectionRange({Cursor, Cursor}); 155 RefContext.setSelectionRange({Cursor, Cursor});
@@ -181,7 +160,7 @@ TEST_F(RefactoringActionRulesTest, ReturnInitiationDiagnostic) {
181 llvm::handleAllErrors(Result.takeError(), [&](llvm::StringError &Error) { 160 llvm::handleAllErrors(Result.takeError(), [&](llvm::StringError &Error) {
182 Message = Error.getMessage(); 161 Message = Error.getMessage();
183 }); 162 });
184 EXPECT_EQ(Message, "bad selection"); 163 EXPECT_EQ(Message, "Error");
185} 164}
186 165
187Optional<SymbolOccurrences> findOccurrences(RefactoringActionRule &Rule, 166Optional<SymbolOccurrences> findOccurrences(RefactoringActionRule &Rule,
@@ -205,18 +184,24 @@ Optional<SymbolOccurrences> findOccurrences(RefactoringActionRule &Rule,
205} 184}
206 185
207TEST_F(RefactoringActionRulesTest, ReturnSymbolOccurrences) { 186TEST_F(RefactoringActionRulesTest, ReturnSymbolOccurrences) {
208 auto Rule = createRefactoringRule( 187 class FindOccurrences : public FindSymbolOccurrencesRefactoringRule {
209 [](const RefactoringRuleContext &, 188 SourceRange Selection;
210 selection::SourceSelectionRange Selection) 189
211 -> Expected<SymbolOccurrences> { 190 public:
212 SymbolOccurrences Occurrences; 191 FindOccurrences(SourceRange Selection) : Selection(Selection) {}
213 Occurrences.push_back(SymbolOccurrence( 192
214 SymbolName("test"), SymbolOccurrence::MatchingSymbol, 193 Expected<SymbolOccurrences>
215 Selection.getRange().getBegin())); 194 findSymbolOccurrences(RefactoringRuleContext &) override {
216 return std::move(Occurrences); 195 SymbolOccurrences Occurrences;
217 }, 196 Occurrences.push_back(SymbolOccurrence(SymbolName("test"),
218 requiredSelection( 197 SymbolOccurrence::MatchingSymbol,
219 selection::identity<selection::SourceSelectionRange>())); 198 Selection.getBegin()));
199 return Occurrences;
200 }
201 };
202
203 auto Rule = createRefactoringActionRule<FindOccurrences>(
204 SourceRangeSelectionRequirement());
220 205
221 RefactoringRuleContext RefContext(Context.Sources); 206 RefactoringRuleContext RefContext(Context.Sources);
222 SourceLocation Cursor = 207 SourceLocation Cursor =