Index: include/clang/Tooling/Refactoring/RefactoringActionRule.h =================================================================== --- include/clang/Tooling/Refactoring/RefactoringActionRule.h +++ include/clang/Tooling/Refactoring/RefactoringActionRule.h @@ -11,52 +11,25 @@ #define LLVM_CLANG_TOOLING_REFACTOR_REFACTORING_ACTION_RULE_H #include "clang/Basic/LLVM.h" -#include "clang/Tooling/Refactoring/AtomicChange.h" -#include "llvm/Support/Error.h" #include namespace clang { namespace tooling { +class RefactoringResultConsumer; class RefactoringRuleContext; /// A common refactoring action rule interface. class RefactoringActionRule { public: - enum RuleKind { SourceChangeRefactoringRuleKind }; - - RuleKind getRuleKind() const { return Kind; } - virtual ~RefactoringActionRule() {} -protected: - RefactoringActionRule(RuleKind Kind) : Kind(Kind) {} - -private: - RuleKind Kind; -}; - -/// A type of refactoring action rule that produces source replacements in the -/// form of atomic changes. -/// -/// This action rule is typically used for local refactorings that replace -/// source in a single AST unit. -class SourceChangeRefactoringRule : public RefactoringActionRule { -public: - SourceChangeRefactoringRule() - : RefactoringActionRule(SourceChangeRefactoringRuleKind) {} - - /// Initiates and performs a refactoring action that modifies the sources. + /// Initiates and performs a specific refactoring action. /// - /// 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 AtomicChanges when the action was performed successfully. - virtual Expected> - createSourceReplacements(RefactoringRuleContext &Context) = 0; - - static bool classof(const RefactoringActionRule *Rule) { - return Rule->getRuleKind() == SourceChangeRefactoringRuleKind; - } + /// The specific rule will invoke an appropriate \c handle method on a + /// consumer to propagate the result of the refactoring action. + virtual void invoke(RefactoringResultConsumer &Consumer, + RefactoringRuleContext &Context) = 0; }; /// A set of refactoring action rules that should have unique initiation Index: include/clang/Tooling/Refactoring/RefactoringActionRules.h =================================================================== --- include/clang/Tooling/Refactoring/RefactoringActionRules.h +++ include/clang/Tooling/Refactoring/RefactoringActionRules.h @@ -42,15 +42,12 @@ createRefactoringRule(Expected (*RefactoringFunction)( typename RequirementTypes::OutputType...), const RequirementTypes &... Requirements) { - static_assert( - std::is_base_of< - RefactoringActionRule, - internal::SpecificRefactoringRuleAdapter>::value, - "invalid refactoring result type"); + static_assert(tooling::traits::IsValidRefactoringResult::value, + "invalid refactoring result type"); static_assert(traits::IsRequirement::value, "invalid refactoring action rule requirement"); return llvm::make_unique>( + decltype(RefactoringFunction), RequirementTypes...>>( RefactoringFunction, std::make_tuple(Requirements...)); } Index: include/clang/Tooling/Refactoring/RefactoringActionRulesInternal.h =================================================================== --- include/clang/Tooling/Refactoring/RefactoringActionRulesInternal.h +++ include/clang/Tooling/Refactoring/RefactoringActionRulesInternal.h @@ -13,6 +13,7 @@ #include "clang/Basic/LLVM.h" #include "clang/Tooling/Refactoring/RefactoringActionRule.h" #include "clang/Tooling/Refactoring/RefactoringActionRuleRequirements.h" +#include "clang/Tooling/Refactoring/RefactoringResultConsumer.h" #include "clang/Tooling/Refactoring/RefactoringRuleContext.h" #include "llvm/Support/Error.h" #include @@ -22,39 +23,20 @@ namespace refactoring_action_rules { namespace internal { -/// A wrapper around a specific refactoring action rule that calls a generic -/// '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); - } -}; - /// A specialized refactoring action rule that calls the stored function once /// all the of the requirements are fullfilled. The values produced during the /// evaluation of requirements are passed to the stored function. -template -class PlainFunctionRule final - : public SpecificRefactoringRuleAdapter { +template +class PlainFunctionRule final : public RefactoringActionRule { public: PlainFunctionRule(FunctionType Function, std::tuple &&Requirements) : Function(Function), Requirements(std::move(Requirements)) {} - Expected> - perform(RefactoringRuleContext &Context) override { - return performImpl(Context, - llvm::index_sequence_for()); + void invoke(RefactoringResultConsumer &Consumer, + RefactoringRuleContext &Context) override { + return invokeImpl(Consumer, Context, + llvm::index_sequence_for()); } private: @@ -95,8 +77,9 @@ } template - Expected> performImpl(RefactoringRuleContext &Context, - llvm::index_sequence) { + void invokeImpl(RefactoringResultConsumer &Consumer, + RefactoringRuleContext &Context, + llvm::index_sequence) { // Initiate the operation. auto Values = std::make_tuple(std::get(Requirements).evaluate(Context)...); @@ -105,12 +88,15 @@ if (InitiationFailure) { llvm::Error Error = std::move(*InitiationFailure); if (!Error) - return None; - return std::move(Error); + return Consumer.handleInitiationFailure(); + return Consumer.handleInitiationError(std::move(Error)); } // Perform the operation. - return Function( - unwrapRequirementResult(std::move(std::get(Values)))...); + auto Result = + Function(unwrapRequirementResult(std::move(std::get(Values)))...); + if (!Result) + return Consumer.handleInvocationError(Result.takeError()); + Consumer.handle(std::move(*Result)); } FunctionType Function; Index: include/clang/Tooling/Refactoring/RefactoringResultConsumer.h =================================================================== --- /dev/null +++ include/clang/Tooling/Refactoring/RefactoringResultConsumer.h @@ -0,0 +1,68 @@ +//===--- RefactoringResultConsumer.h - Clang refactoring library ----------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_TOOLING_REFACTOR_REFACTORING_RESULT_CONSUMER_H +#define LLVM_CLANG_TOOLING_REFACTOR_REFACTORING_RESULT_CONSUMER_H + +#include "clang/Basic/LLVM.h" +#include "clang/Tooling/Refactoring/AtomicChange.h" +#include "llvm/Support/Error.h" + +namespace clang { +namespace tooling { + +/// An abstract interface that consumes the various refactoring results that can +/// be produced by refactoring actions. +/// +/// A valid refactoring result must be handled by a \c handle method. +class RefactoringResultConsumer { +public: + virtual ~RefactoringResultConsumer() {} + + /// Handles an initiation failure. An action typically fails to initiate when + /// the source selection has no overlap at all with any relevant AST nodes. + virtual void handleInitiationFailure() = 0; + + /// Handles an initation error. The error typically has a \c DiagnosticError + /// payload that describes why initation failed. + virtual void handleInitiationError(llvm::Error Err) = 0; + + virtual void handleInvocationError(llvm::Error Err) = 0; + + /// Handles the source replacements that are produced by a refactoring action. + virtual void handle(AtomicChanges SourceReplacements) = 0; +}; + +namespace traits { +namespace internal { + +template struct HasHandle { +private: + template + static auto check(ClassT *) -> typename std::is_same< + decltype(std::declval().handle(std::declval())), void>::type; + + template static std::false_type check(...); + +public: + using Type = decltype(check(nullptr)); +}; + +} // end namespace internal + +/// A type trait that returns true iff the given type is a valid refactoring +/// result. +template +struct IsValidRefactoringResult : internal::HasHandle::Type {}; + +} // end namespace traits +} // end namespace tooling +} // end namespace clang + +#endif // LLVM_CLANG_TOOLING_REFACTOR_REFACTORING_RESULT_CONSUMER_H Index: unittests/Tooling/RefactoringActionRulesTest.cpp =================================================================== --- unittests/Tooling/RefactoringActionRulesTest.cpp +++ unittests/Tooling/RefactoringActionRulesTest.cpp @@ -35,8 +35,27 @@ Expected> createReplacements(const std::unique_ptr &Rule, RefactoringRuleContext &Context) { - return cast(*Rule).createSourceReplacements( - Context); + class Consumer final : public RefactoringResultConsumer { + void handleInitiationFailure() { + Result = Expected>(None); + } + void handleInitiationError(llvm::Error Err) { Result = std::move(Err); } + + void handleInvocationError(llvm::Error Err) { + handleInitiationError(std::move(Err)); + } + + void handle(AtomicChanges SourceReplacements) { + Result = std::move(SourceReplacements); + } + + public: + Optional>> Result; + }; + + Consumer C; + Rule->invoke(C, Context); + return std::move(*C.Result); } TEST_F(RefactoringActionRulesTest, MyFirstRefactoringRule) {