r311884 - [refactor] initial support for refactoring action rules
Alex Lorenz via cfe-commits
cfe-commits at lists.llvm.org
Mon Aug 28 04:12:05 PDT 2017
Author: arphaman
Date: Mon Aug 28 04:12:05 2017
New Revision: 311884
URL: http://llvm.org/viewvc/llvm-project?rev=311884&view=rev
Log:
[refactor] initial support for refactoring action rules
This patch implements the initial support for refactoring action rules. The
first rule that's supported is a "source change" rule that returns a set of
atomic changes. This patch is based on the ideas presented in my RFC:
http://lists.llvm.org/pipermail/cfe-dev/2017-July/054831.html
The following pieces from the RFC are added by this patch:
- `createRefactoringRule` (known as `apply` in the RFC)
- `requiredSelection` refactoring action rule requirement.
- `selection::SourceSelectionRange` selection constraint.
Differential Revision: https://reviews.llvm.org/D36075
Added:
cfe/trunk/include/clang/Tooling/Refactoring/RefactoringActionRule.h
cfe/trunk/include/clang/Tooling/Refactoring/RefactoringActionRuleRequirements.h
cfe/trunk/include/clang/Tooling/Refactoring/RefactoringActionRuleRequirementsInternal.h
cfe/trunk/include/clang/Tooling/Refactoring/RefactoringActionRules.h
cfe/trunk/include/clang/Tooling/Refactoring/RefactoringActionRulesInternal.h
cfe/trunk/include/clang/Tooling/Refactoring/RefactoringRuleContext.h
cfe/trunk/include/clang/Tooling/Refactoring/SourceSelectionConstraints.h
cfe/trunk/lib/Tooling/Refactoring/SourceSelectionConstraints.cpp
cfe/trunk/unittests/Tooling/RefactoringActionRulesTest.cpp
Modified:
cfe/trunk/include/clang/Basic/LLVM.h
cfe/trunk/include/clang/Tooling/Refactoring/AtomicChange.h
cfe/trunk/lib/Tooling/Refactoring/CMakeLists.txt
cfe/trunk/unittests/Tooling/CMakeLists.txt
Modified: cfe/trunk/include/clang/Basic/LLVM.h
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Basic/LLVM.h?rev=311884&r1=311883&r2=311884&view=diff
==============================================================================
--- cfe/trunk/include/clang/Basic/LLVM.h (original)
+++ cfe/trunk/include/clang/Basic/LLVM.h Mon Aug 28 04:12:05 2017
@@ -35,6 +35,7 @@ namespace llvm {
template<typename T, unsigned N> class SmallVector;
template<typename T> class SmallVectorImpl;
template<typename T> class Optional;
+ template <class T> class Expected;
template<typename T>
struct SaveAndRestore;
@@ -71,6 +72,9 @@ namespace clang {
using llvm::SmallVectorImpl;
using llvm::SaveAndRestore;
+ // Error handling.
+ using llvm::Expected;
+
// Reference counting.
using llvm::IntrusiveRefCntPtr;
using llvm::IntrusiveRefCntPtrInfo;
Modified: cfe/trunk/include/clang/Tooling/Refactoring/AtomicChange.h
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Tooling/Refactoring/AtomicChange.h?rev=311884&r1=311883&r2=311884&view=diff
==============================================================================
--- cfe/trunk/include/clang/Tooling/Refactoring/AtomicChange.h (original)
+++ cfe/trunk/include/clang/Tooling/Refactoring/AtomicChange.h Mon Aug 28 04:12:05 2017
@@ -46,6 +46,12 @@ public:
AtomicChange(llvm::StringRef FilePath, llvm::StringRef Key)
: Key(Key), FilePath(FilePath) {}
+ AtomicChange(AtomicChange &&) = default;
+ AtomicChange(const AtomicChange &) = default;
+
+ AtomicChange &operator=(AtomicChange &&) = default;
+ AtomicChange &operator=(const AtomicChange &) = default;
+
/// \brief Returns the atomic change as a YAML string.
std::string toYAMLString();
@@ -130,6 +136,8 @@ private:
tooling::Replacements Replaces;
};
+using AtomicChanges = std::vector<AtomicChange>;
+
// Defines specs for applying changes.
struct ApplyChangesSpec {
// If true, cleans up redundant/erroneous code around changed code with
Added: cfe/trunk/include/clang/Tooling/Refactoring/RefactoringActionRule.h
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Tooling/Refactoring/RefactoringActionRule.h?rev=311884&view=auto
==============================================================================
--- cfe/trunk/include/clang/Tooling/Refactoring/RefactoringActionRule.h (added)
+++ cfe/trunk/include/clang/Tooling/Refactoring/RefactoringActionRule.h Mon Aug 28 04:12:05 2017
@@ -0,0 +1,70 @@
+//===--- RefactoringActionRule.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_ACTION_RULE_H
+#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 <vector>
+
+namespace clang {
+namespace tooling {
+
+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.
+ ///
+ /// 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<Optional<AtomicChanges>>
+ createSourceReplacements(RefactoringRuleContext &Context) = 0;
+
+ static bool classof(const RefactoringActionRule *Rule) {
+ return Rule->getRuleKind() == SourceChangeRefactoringRuleKind;
+ }
+};
+
+/// A set of refactoring action rules that should have unique initiation
+/// requirements.
+using RefactoringActionRules =
+ std::vector<std::unique_ptr<RefactoringActionRule>>;
+
+} // end namespace tooling
+} // end namespace clang
+
+#endif // LLVM_CLANG_TOOLING_REFACTOR_REFACTORING_ACTION_RULE_H
Added: cfe/trunk/include/clang/Tooling/Refactoring/RefactoringActionRuleRequirements.h
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Tooling/Refactoring/RefactoringActionRuleRequirements.h?rev=311884&view=auto
==============================================================================
--- cfe/trunk/include/clang/Tooling/Refactoring/RefactoringActionRuleRequirements.h (added)
+++ cfe/trunk/include/clang/Tooling/Refactoring/RefactoringActionRuleRequirements.h Mon Aug 28 04:12:05 2017
@@ -0,0 +1,58 @@
+//===--- RefactoringActionRuleRequirements.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_ACTION_RULE_REQUIREMENTS_H
+#define LLVM_CLANG_TOOLING_REFACTOR_REFACTORING_ACTION_RULE_REQUIREMENTS_H
+
+#include "clang/Tooling/Refactoring/RefactoringActionRuleRequirementsInternal.h"
+#include "llvm/Support/Error.h"
+#include <type_traits>
+
+namespace clang {
+namespace tooling {
+namespace refactoring_action_rules {
+
+/// Creates a selection requirement from the given requirement.
+///
+/// Requirements must subclass \c selection::Requirement and implement
+/// evaluateSelection member function.
+template <typename T>
+internal::SourceSelectionRequirement<
+ typename selection::internal::EvaluateSelectionChecker<
+ decltype(&T::evaluateSelection)>::ArgType,
+ typename selection::internal::EvaluateSelectionChecker<
+ decltype(&T::evaluateSelection)>::ReturnType,
+ T>
+requiredSelection(
+ const T &Requirement,
+ typename std::enable_if<selection::traits::IsRequirement<T>::value>::type
+ * = nullptr) {
+ return internal::SourceSelectionRequirement<
+ typename selection::internal::EvaluateSelectionChecker<decltype(
+ &T::evaluateSelection)>::ArgType,
+ typename selection::internal::EvaluateSelectionChecker<decltype(
+ &T::evaluateSelection)>::ReturnType,
+ T>(Requirement);
+}
+
+template <typename T>
+void requiredSelection(
+ const T &,
+ typename std::enable_if<
+ !std::is_base_of<selection::Requirement, T>::value>::type * = nullptr) {
+ static_assert(
+ sizeof(T) && false,
+ "selection requirement must be a class derived from Requirement");
+}
+
+} // end namespace refactoring_action_rules
+} // end namespace tooling
+} // end namespace clang
+
+#endif // LLVM_CLANG_TOOLING_REFACTOR_REFACTORING_ACTION_RULE_REQUIREMENTS_H
Added: cfe/trunk/include/clang/Tooling/Refactoring/RefactoringActionRuleRequirementsInternal.h
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Tooling/Refactoring/RefactoringActionRuleRequirementsInternal.h?rev=311884&view=auto
==============================================================================
--- cfe/trunk/include/clang/Tooling/Refactoring/RefactoringActionRuleRequirementsInternal.h (added)
+++ cfe/trunk/include/clang/Tooling/Refactoring/RefactoringActionRuleRequirementsInternal.h Mon Aug 28 04:12:05 2017
@@ -0,0 +1,90 @@
+//===--- RefactoringActionRuleRequirementsInternal.h - --------------------===//
+//
+// 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_ACTION_REQUIREMENTS_INTERNAL_H
+#define LLVM_CLANG_TOOLING_REFACTOR_REFACTORING_ACTION_REQUIREMENTS_INTERNAL_H
+
+#include "clang/Basic/LLVM.h"
+#include "clang/Tooling/Refactoring/RefactoringRuleContext.h"
+#include "clang/Tooling/Refactoring/SourceSelectionConstraints.h"
+#include <type_traits>
+
+namespace clang {
+namespace tooling {
+namespace refactoring_action_rules {
+namespace internal {
+
+/// A base class for any requirement. Used by the \c IsRequirement trait to
+/// determine if a class is a valid requirement.
+struct RequirementBase {};
+
+/// Defines a type alias of type \T when given \c Expected<Optional<T>>, or
+/// \c T otherwise.
+template <typename T> struct DropExpectedOptional { using Type = T; };
+
+template <typename T> struct DropExpectedOptional<Expected<Optional<T>>> {
+ using Type = T;
+};
+
+/// The \c requiredSelection refactoring action requirement is represented
+/// using this type.
+template <typename InputT, typename OutputT, typename RequirementT>
+struct SourceSelectionRequirement
+ : std::enable_if<selection::traits::IsConstraint<InputT>::value &&
+ selection::traits::IsRequirement<RequirementT>::value,
+ RequirementBase>::type {
+ using OutputType = typename DropExpectedOptional<OutputT>::Type;
+
+ SourceSelectionRequirement(const RequirementT &Requirement)
+ : Requirement(Requirement) {}
+
+ /// Evaluates the action rule requirement by ensuring that both the selection
+ /// constraint and the selection requirement can be evaluated with the given
+ /// context.
+ ///
+ /// \returns None if the selection constraint is not evaluated successfully,
+ /// Error if the selection requirement is not evaluated successfully or
+ /// an OutputT if the selection requirement was successfully. The OutpuT
+ /// value is wrapped in Expected<Optional<>> which is then unwrapped by the
+ /// refactoring action rule before passing the value to the refactoring
+ /// function.
+ Expected<Optional<OutputType>> evaluate(RefactoringRuleContext &Context) {
+ Optional<InputT> Value = InputT::evaluate(Context);
+ if (!Value)
+ return None;
+ return std::move(Requirement.evaluateSelection(*Value));
+ }
+
+private:
+ const RequirementT Requirement;
+};
+
+} // end namespace internal
+
+namespace traits {
+
+/// A type trait that returns true iff the given type is a valid rule
+/// requirement.
+template <typename First, typename... Rest>
+struct IsRequirement : std::conditional<IsRequirement<First>::value &&
+ IsRequirement<Rest...>::value,
+ std::true_type, std::false_type>::type {
+};
+
+template <typename T>
+struct IsRequirement<T>
+ : std::conditional<std::is_base_of<internal::RequirementBase, T>::value,
+ std::true_type, std::false_type>::type {};
+
+} // end namespace traits
+} // end namespace refactoring_action_rules
+} // end namespace tooling
+} // end namespace clang
+
+#endif // LLVM_CLANG_TOOLING_REFACTOR_REFACTORING_ACTION_REQUIREMENTS_INTERNAL_H
Added: cfe/trunk/include/clang/Tooling/Refactoring/RefactoringActionRules.h
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Tooling/Refactoring/RefactoringActionRules.h?rev=311884&view=auto
==============================================================================
--- cfe/trunk/include/clang/Tooling/Refactoring/RefactoringActionRules.h (added)
+++ cfe/trunk/include/clang/Tooling/Refactoring/RefactoringActionRules.h Mon Aug 28 04:12:05 2017
@@ -0,0 +1,76 @@
+//===--- RefactoringActionRules.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_ACTION_RULES_H
+#define LLVM_CLANG_TOOLING_REFACTOR_REFACTORING_ACTION_RULES_H
+
+#include "clang/Tooling/Refactoring/RefactoringActionRule.h"
+#include "clang/Tooling/Refactoring/RefactoringActionRulesInternal.h"
+
+namespace clang {
+namespace tooling {
+namespace refactoring_action_rules {
+
+/// Creates a new refactoring action rule that invokes the given function once
+/// all of the requirements are satisfied. The values produced during the
+/// evaluation of requirements are passed to the given function (in the order of
+/// requirements).
+///
+/// \param RefactoringFunction the function that will perform the refactoring
+/// once the requirements are satisfied. The function must return a valid
+/// refactoring result type wrapped in an \c Expected type. The following result
+/// types are currently supported:
+///
+/// - AtomicChanges: the refactoring function will be used to create source
+/// replacements.
+///
+/// \param Requirements a set of rule requirements that have to be satisfied.
+/// Each requirement must be a valid requirement, i.e. the value of
+/// \c traits::IsRequirement<T> must be true. The following requirements are
+/// currently supported:
+///
+/// - requiredSelection: The refactoring function won't be invoked unless the
+/// given selection requirement is satisfied.
+template <typename ResultType, typename... RequirementTypes>
+std::unique_ptr<RefactoringActionRule>
+createRefactoringRule(Expected<ResultType> (*RefactoringFunction)(
+ typename RequirementTypes::OutputType...),
+ const RequirementTypes &... Requirements) {
+ static_assert(
+ std::is_base_of<
+ RefactoringActionRule,
+ internal::SpecificRefactoringRuleAdapter<ResultType>>::value,
+ "invalid refactoring result type");
+ static_assert(traits::IsRequirement<RequirementTypes...>::value,
+ "invalid refactoring action rule requirement");
+ return llvm::make_unique<internal::PlainFunctionRule<
+ ResultType, decltype(RefactoringFunction), RequirementTypes...>>(
+ RefactoringFunction, std::make_tuple(Requirements...));
+}
+
+template <
+ typename Callable, typename... RequirementTypes,
+ typename Fn = decltype(&Callable::operator()),
+ typename ResultType = typename internal::LambdaDeducer<Fn>::ReturnType,
+ bool IsNonCapturingLambda = std::is_convertible<
+ Callable,
+ ResultType (*)(typename RequirementTypes::OutputType...)>::value,
+ typename = typename std::enable_if<IsNonCapturingLambda>::type>
+std::unique_ptr<RefactoringActionRule>
+createRefactoringRule(const Callable &C,
+ const RequirementTypes &... Requirements) {
+ ResultType (*Func)(typename RequirementTypes::OutputType...) = C;
+ return createRefactoringRule(Func, Requirements...);
+}
+
+} // end namespace refactoring_action_rules
+} // end namespace tooling
+} // end namespace clang
+
+#endif // LLVM_CLANG_TOOLING_REFACTOR_REFACTORING_ACTION_RULES_H
Added: cfe/trunk/include/clang/Tooling/Refactoring/RefactoringActionRulesInternal.h
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Tooling/Refactoring/RefactoringActionRulesInternal.h?rev=311884&view=auto
==============================================================================
--- cfe/trunk/include/clang/Tooling/Refactoring/RefactoringActionRulesInternal.h (added)
+++ cfe/trunk/include/clang/Tooling/Refactoring/RefactoringActionRulesInternal.h Mon Aug 28 04:12:05 2017
@@ -0,0 +1,133 @@
+//===--- RefactoringActionRulesInternal.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_ACTION_RULES_INTERNAL_H
+#define LLVM_CLANG_TOOLING_REFACTOR_REFACTORING_ACTION_RULES_INTERNAL_H
+
+#include "clang/Basic/LLVM.h"
+#include "clang/Tooling/Refactoring/RefactoringActionRule.h"
+#include "clang/Tooling/Refactoring/RefactoringActionRuleRequirements.h"
+#include "clang/Tooling/Refactoring/RefactoringRuleContext.h"
+#include "llvm/Support/Error.h"
+#include <type_traits>
+
+namespace clang {
+namespace tooling {
+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 <typename T> struct SpecificRefactoringRuleAdapter {};
+
+template <>
+class SpecificRefactoringRuleAdapter<AtomicChanges>
+ : public SourceChangeRefactoringRule {
+public:
+ virtual Expected<Optional<AtomicChanges>>
+ perform(RefactoringRuleContext &Context) = 0;
+
+ Expected<Optional<AtomicChanges>>
+ 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 <typename ResultType, typename FunctionType,
+ typename... RequirementTypes>
+class PlainFunctionRule final
+ : public SpecificRefactoringRuleAdapter<ResultType> {
+public:
+ PlainFunctionRule(FunctionType Function,
+ std::tuple<RequirementTypes...> &&Requirements)
+ : Function(Function), Requirements(std::move(Requirements)) {}
+
+ Expected<Optional<ResultType>>
+ perform(RefactoringRuleContext &Context) override {
+ return performImpl(Context,
+ llvm::index_sequence_for<RequirementTypes...>());
+ }
+
+private:
+ /// Returns \c T when given \c Expected<Optional<T>>, or \c T otherwise.
+ template <typename T>
+ static T &&unwrapRequirementResult(Expected<Optional<T>> &&X) {
+ assert(X && "unexpected diagnostic!");
+ return std::move(**X);
+ }
+ template <typename T> static T &&unwrapRequirementResult(T &&X) {
+ return std::move(X);
+ }
+
+ /// Scans the tuple and returns a \c PartialDiagnosticAt
+ /// from the first invalid \c DiagnosticOr value. Returns \c None if all
+ /// values are valid.
+ template <typename FirstT, typename... RestT>
+ static Optional<llvm::Error> findErrorNone(FirstT &First, RestT &... Rest) {
+ Optional<llvm::Error> Result = takeErrorNone(First);
+ if (Result)
+ return Result;
+ return findErrorNone(Rest...);
+ }
+
+ static Optional<llvm::Error> findErrorNone() { return None; }
+
+ template <typename T> static Optional<llvm::Error> takeErrorNone(T &) {
+ return None;
+ }
+
+ template <typename T>
+ static Optional<llvm::Error> takeErrorNone(Expected<Optional<T>> &Diag) {
+ if (!Diag)
+ return std::move(Diag.takeError());
+ if (!*Diag)
+ return llvm::Error::success(); // Initiation failed without a diagnostic.
+ return None;
+ }
+
+ template <size_t... Is>
+ Expected<Optional<ResultType>> performImpl(RefactoringRuleContext &Context,
+ llvm::index_sequence<Is...>) {
+ // Initiate the operation.
+ auto Values =
+ std::make_tuple(std::get<Is>(Requirements).evaluate(Context)...);
+ Optional<llvm::Error> InitiationFailure =
+ findErrorNone(std::get<Is>(Values)...);
+ if (InitiationFailure) {
+ llvm::Error Error = std::move(*InitiationFailure);
+ if (!Error)
+ return None;
+ return std::move(Error);
+ }
+ // Perform the operation.
+ return Function(
+ unwrapRequirementResult(std::move(std::get<Is>(Values)))...);
+ }
+
+ FunctionType Function;
+ std::tuple<RequirementTypes...> Requirements;
+};
+
+/// Used to deduce the refactoring result type for the lambda that passed into
+/// createRefactoringRule.
+template <typename T> struct LambdaDeducer;
+template <typename T, typename R, typename... Args>
+struct LambdaDeducer<R (T::*)(Args...) const> {
+ using ReturnType = R;
+};
+
+} // end namespace internal
+} // end namespace refactoring_action_rules
+} // end namespace tooling
+} // end namespace clang
+
+#endif // LLVM_CLANG_TOOLING_REFACTOR_REFACTORING_ACTION_RULES_INTERNAL_H
Added: cfe/trunk/include/clang/Tooling/Refactoring/RefactoringRuleContext.h
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Tooling/Refactoring/RefactoringRuleContext.h?rev=311884&view=auto
==============================================================================
--- cfe/trunk/include/clang/Tooling/Refactoring/RefactoringRuleContext.h (added)
+++ cfe/trunk/include/clang/Tooling/Refactoring/RefactoringRuleContext.h Mon Aug 28 04:12:05 2017
@@ -0,0 +1,53 @@
+//===--- RefactoringRuleContext.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_RULE_CONTEXT_H
+#define LLVM_CLANG_TOOLING_REFACTOR_REFACTORING_RULE_CONTEXT_H
+
+#include "clang/Basic/SourceManager.h"
+
+namespace clang {
+namespace tooling {
+
+/// The refactoring rule context stores all of the inputs that might be needed
+/// by a refactoring action rule. It can create the specialized
+/// \c ASTRefactoringOperation or \c PreprocessorRefactoringOperation values
+/// that can be used by the refactoring action rules.
+///
+/// The following inputs are stored by the operation:
+///
+/// - SourceManager: a reference to a valid source manager.
+///
+/// - SelectionRange: an optional source selection ranges that can be used
+/// to represent a selection in an editor.
+class RefactoringRuleContext {
+public:
+ RefactoringRuleContext(const SourceManager &SM) : SM(SM) {}
+
+ const SourceManager &getSources() const { return SM; }
+
+ /// Returns the current source selection range as set by the
+ /// refactoring engine. Can be invalid.
+ SourceRange getSelectionRange() const { return SelectionRange; }
+
+ void setSelectionRange(SourceRange R) { SelectionRange = R; }
+
+private:
+ /// The source manager for the translation unit / file on which a refactoring
+ /// action might operate on.
+ const SourceManager &SM;
+ /// An optional source selection range that's commonly used to represent
+ /// a selection in an editor.
+ SourceRange SelectionRange;
+};
+
+} // end namespace tooling
+} // end namespace clang
+
+#endif // LLVM_CLANG_TOOLING_REFACTOR_REFACTORING_RULE_CONTEXT_H
Added: cfe/trunk/include/clang/Tooling/Refactoring/SourceSelectionConstraints.h
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Tooling/Refactoring/SourceSelectionConstraints.h?rev=311884&view=auto
==============================================================================
--- cfe/trunk/include/clang/Tooling/Refactoring/SourceSelectionConstraints.h (added)
+++ cfe/trunk/include/clang/Tooling/Refactoring/SourceSelectionConstraints.h Mon Aug 28 04:12:05 2017
@@ -0,0 +1,109 @@
+//===--- SourceSelectionConstraints.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_SOURCE_SELECTION_CONSTRAINTS_H
+#define LLVM_CLANG_TOOLING_REFACTOR_SOURCE_SELECTION_CONSTRAINTS_H
+
+#include "clang/Basic/LLVM.h"
+#include "clang/Basic/SourceLocation.h"
+#include <type_traits>
+
+namespace clang {
+namespace tooling {
+
+class RefactoringRuleContext;
+
+namespace selection {
+
+/// This constraint is satisfied when any portion of the source text is
+/// selected. It can be used to obtain the raw source selection range.
+struct SourceSelectionRange {
+ SourceSelectionRange(const SourceManager &SM, SourceRange Range)
+ : SM(SM), Range(Range) {}
+
+ const SourceManager &getSources() const { return SM; }
+ SourceRange getRange() const { return Range; }
+
+ static Optional<SourceSelectionRange>
+ evaluate(RefactoringRuleContext &Context);
+
+private:
+ const SourceManager &SM;
+ SourceRange Range;
+};
+
+/// A custom selection requirement.
+class Requirement {
+ /// Subclasses must implement 'T evaluateSelection(SelectionConstraint) const'
+ /// member function. \c T is used to determine the return type that is
+ /// passed to the refactoring rule's function.
+ /// If T is \c DiagnosticOr<S> , then \c S is passed to the rule's function
+ /// using move semantics.
+ /// Otherwise, T is passed to the function directly using move semantics.
+ ///
+ /// The different return type rules allow refactoring actions to fail
+ /// initiation when the relevant portions of AST aren't selected.
+};
+
+namespace traits {
+
+/// A type trait that returns true iff the given type is a valid selection
+/// constraint.
+template <typename T> struct IsConstraint : public std::false_type {};
+
+} // end namespace traits
+
+namespace internal {
+
+template <typename T> struct EvaluateSelectionChecker : std::false_type {};
+
+template <typename T, typename R, typename A>
+struct EvaluateSelectionChecker<R (T::*)(A) const> : std::true_type {
+ using ReturnType = R;
+ using ArgType = A;
+};
+
+template <typename T> class Identity : public Requirement {
+public:
+ T evaluateSelection(T Value) const { return std::move(Value); }
+};
+
+} // end namespace internal
+
+/// A identity function that returns the given selection constraint is provided
+/// for convenience, as it can be passed to \c requiredSelection directly.
+template <typename T> internal::Identity<T> identity() {
+ static_assert(
+ traits::IsConstraint<T>::value,
+ "selection::identity can be used with selection constraints only");
+ return internal::Identity<T>();
+}
+
+namespace traits {
+
+template <>
+struct IsConstraint<SourceSelectionRange> : public std::true_type {};
+
+/// A type trait that returns true iff \c T is a valid selection requirement.
+template <typename T>
+struct IsRequirement
+ : std::conditional<
+ std::is_base_of<Requirement, T>::value &&
+ internal::EvaluateSelectionChecker<decltype(
+ &T::evaluateSelection)>::value &&
+ IsConstraint<typename internal::EvaluateSelectionChecker<decltype(
+ &T::evaluateSelection)>::ArgType>::value,
+ std::true_type, std::false_type>::type {};
+
+} // end namespace traits
+} // end namespace selection
+} // end namespace tooling
+} // end namespace clang
+
+#endif // LLVM_CLANG_TOOLING_REFACTOR_SOURCE_SELECTION_CONSTRAINTS_H
Modified: cfe/trunk/lib/Tooling/Refactoring/CMakeLists.txt
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Tooling/Refactoring/CMakeLists.txt?rev=311884&r1=311883&r2=311884&view=diff
==============================================================================
--- cfe/trunk/lib/Tooling/Refactoring/CMakeLists.txt (original)
+++ cfe/trunk/lib/Tooling/Refactoring/CMakeLists.txt Mon Aug 28 04:12:05 2017
@@ -8,6 +8,7 @@ add_clang_library(clangToolingRefactor
Rename/USRFinder.cpp
Rename/USRFindingAction.cpp
Rename/USRLocFinder.cpp
+ SourceSelectionConstraints.cpp
LINK_LIBS
clangAST
Added: cfe/trunk/lib/Tooling/Refactoring/SourceSelectionConstraints.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Tooling/Refactoring/SourceSelectionConstraints.cpp?rev=311884&view=auto
==============================================================================
--- cfe/trunk/lib/Tooling/Refactoring/SourceSelectionConstraints.cpp (added)
+++ cfe/trunk/lib/Tooling/Refactoring/SourceSelectionConstraints.cpp Mon Aug 28 04:12:05 2017
@@ -0,0 +1,23 @@
+//===--- SourceSelectionConstraints.cpp - Evaluate selection constraints --===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "clang/Tooling/Refactoring/SourceSelectionConstraints.h"
+#include "clang/Tooling/Refactoring/RefactoringRuleContext.h"
+
+using namespace clang;
+using namespace tooling;
+using namespace selection;
+
+Optional<SourceSelectionRange>
+SourceSelectionRange::evaluate(RefactoringRuleContext &Context) {
+ SourceRange R = Context.getSelectionRange();
+ if (R.isInvalid())
+ return None;
+ return SourceSelectionRange(Context.getSources(), R);
+}
Modified: cfe/trunk/unittests/Tooling/CMakeLists.txt
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/unittests/Tooling/CMakeLists.txt?rev=311884&r1=311883&r2=311884&view=diff
==============================================================================
--- cfe/trunk/unittests/Tooling/CMakeLists.txt (original)
+++ cfe/trunk/unittests/Tooling/CMakeLists.txt Mon Aug 28 04:12:05 2017
@@ -25,6 +25,7 @@ add_clang_unittest(ToolingTests
RecursiveASTVisitorTestDeclVisitor.cpp
RecursiveASTVisitorTestExprVisitor.cpp
RecursiveASTVisitorTestTypeLocVisitor.cpp
+ RefactoringActionRulesTest.cpp
RefactoringCallbacksTest.cpp
RefactoringTest.cpp
ReplacementsYamlTest.cpp
Added: cfe/trunk/unittests/Tooling/RefactoringActionRulesTest.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/unittests/Tooling/RefactoringActionRulesTest.cpp?rev=311884&view=auto
==============================================================================
--- cfe/trunk/unittests/Tooling/RefactoringActionRulesTest.cpp (added)
+++ cfe/trunk/unittests/Tooling/RefactoringActionRulesTest.cpp Mon Aug 28 04:12:05 2017
@@ -0,0 +1,165 @@
+//===- unittest/Tooling/RefactoringTestActionRulesTest.cpp ----------------===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "ReplacementTest.h"
+#include "RewriterTestContext.h"
+#include "clang/Tooling/Refactoring.h"
+#include "clang/Tooling/Refactoring/RefactoringActionRules.h"
+#include "clang/Tooling/Tooling.h"
+#include "llvm/Support/Errc.h"
+#include "gtest/gtest.h"
+
+using namespace clang;
+using namespace tooling;
+using namespace refactoring_action_rules;
+
+namespace {
+
+class RefactoringActionRulesTest : public ::testing::Test {
+protected:
+ void SetUp() override {
+ Context.Sources.setMainFileID(
+ Context.createInMemoryFile("input.cpp", DefaultCode));
+ }
+
+ RewriterTestContext Context;
+ std::string DefaultCode = std::string(100, 'a');
+};
+
+Expected<Optional<AtomicChanges>>
+createReplacements(const std::unique_ptr<RefactoringActionRule> &Rule,
+ RefactoringRuleContext &Context) {
+ return cast<SourceChangeRefactoringRule>(*Rule).createSourceReplacements(
+ Context);
+}
+
+TEST_F(RefactoringActionRulesTest, MyFirstRefactoringRule) {
+ auto ReplaceAWithB =
+ [](std::pair<selection::SourceSelectionRange, int> Selection)
+ -> Expected<AtomicChanges> {
+ const SourceManager &SM = Selection.first.getSources();
+ SourceLocation Loc = Selection.first.getRange().getBegin().getLocWithOffset(
+ Selection.second);
+ AtomicChange Change(SM, Loc);
+ llvm::Error E = Change.replace(SM, Loc, 1, "b");
+ if (E)
+ return std::move(E);
+ return AtomicChanges{Change};
+ };
+ class SelectionRequirement : public selection::Requirement {
+ public:
+ std::pair<selection::SourceSelectionRange, int>
+ evaluateSelection(selection::SourceSelectionRange Selection) const {
+ return std::make_pair(Selection, 20);
+ }
+ };
+ auto Rule = createRefactoringRule(ReplaceAWithB,
+ requiredSelection(SelectionRequirement()));
+
+ // When the requirements are satisifed, the rule's function must be invoked.
+ {
+ RefactoringRuleContext RefContext(Context.Sources);
+ SourceLocation Cursor =
+ Context.Sources.getLocForStartOfFile(Context.Sources.getMainFileID())
+ .getLocWithOffset(10);
+ RefContext.setSelectionRange({Cursor, Cursor});
+
+ Expected<Optional<AtomicChanges>> ErrorOrResult =
+ createReplacements(Rule, RefContext);
+ ASSERT_FALSE(!ErrorOrResult);
+ ASSERT_FALSE(!*ErrorOrResult);
+ AtomicChanges Result = std::move(**ErrorOrResult);
+ ASSERT_EQ(Result.size(), 1u);
+ std::string YAMLString =
+ const_cast<AtomicChange &>(Result[0]).toYAMLString();
+
+ ASSERT_STREQ("---\n"
+ "Key: 'input.cpp:30'\n"
+ "FilePath: input.cpp\n"
+ "Error: ''\n"
+ "InsertedHeaders: \n"
+ "RemovedHeaders: \n"
+ "Replacements: \n" // Extra whitespace here!
+ " - FilePath: input.cpp\n"
+ " Offset: 30\n"
+ " Length: 1\n"
+ " ReplacementText: b\n"
+ "...\n",
+ YAMLString.c_str());
+ }
+
+ // When one of the requirements is not satisfied, perform should return either
+ // None or a valid diagnostic.
+ {
+ RefactoringRuleContext RefContext(Context.Sources);
+ Expected<Optional<AtomicChanges>> ErrorOrResult =
+ createReplacements(Rule, RefContext);
+
+ ASSERT_FALSE(!ErrorOrResult);
+ Optional<AtomicChanges> Value = std::move(*ErrorOrResult);
+ EXPECT_TRUE(!Value);
+ }
+}
+
+TEST_F(RefactoringActionRulesTest, ReturnError) {
+ Expected<AtomicChanges> (*Func)(selection::SourceSelectionRange) =
+ [](selection::SourceSelectionRange) -> Expected<AtomicChanges> {
+ return llvm::make_error<llvm::StringError>(
+ "Error", std::make_error_code(std::errc::bad_message));
+ };
+ auto Rule = createRefactoringRule(
+ Func, requiredSelection(
+ selection::identity<selection::SourceSelectionRange>()));
+
+ RefactoringRuleContext RefContext(Context.Sources);
+ SourceLocation Cursor =
+ Context.Sources.getLocForStartOfFile(Context.Sources.getMainFileID());
+ RefContext.setSelectionRange({Cursor, Cursor});
+ Expected<Optional<AtomicChanges>> Result =
+ createReplacements(Rule, RefContext);
+
+ ASSERT_TRUE(!Result);
+ std::string Message;
+ llvm::handleAllErrors(Result.takeError(), [&](llvm::StringError &Error) {
+ Message = Error.getMessage();
+ });
+ EXPECT_EQ(Message, "Error");
+}
+
+TEST_F(RefactoringActionRulesTest, ReturnInitiationDiagnostic) {
+ RefactoringRuleContext RefContext(Context.Sources);
+ class SelectionRequirement : public selection::Requirement {
+ public:
+ Expected<Optional<int>>
+ evaluateSelection(selection::SourceSelectionRange Selection) const {
+ return llvm::make_error<llvm::StringError>(
+ "bad selection", std::make_error_code(std::errc::bad_message));
+ }
+ };
+ auto Rule = createRefactoringRule(
+ [](int) -> Expected<AtomicChanges> {
+ llvm::report_fatal_error("Should not run!");
+ },
+ requiredSelection(SelectionRequirement()));
+
+ SourceLocation Cursor =
+ Context.Sources.getLocForStartOfFile(Context.Sources.getMainFileID());
+ RefContext.setSelectionRange({Cursor, Cursor});
+ Expected<Optional<AtomicChanges>> Result =
+ createReplacements(Rule, RefContext);
+
+ ASSERT_TRUE(!Result);
+ std::string Message;
+ llvm::handleAllErrors(Result.takeError(), [&](llvm::StringError &Error) {
+ Message = Error.getMessage();
+ });
+ EXPECT_EQ(Message, "bad selection");
+}
+
+} // end anonymous namespace
More information about the cfe-commits
mailing list