summaryrefslogtreecommitdiffstats
path: root/include/clang/Tooling/Refactoring/RefactoringActionRulesInternal.h
blob: 75b6c8f70d1733701a932e226963ce4485c35f30 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
//===--- 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/RefactoringResultConsumer.h"
#include "clang/Tooling/Refactoring/RefactoringRuleContext.h"
#include "llvm/Support/Error.h"
#include <type_traits>

namespace clang {
namespace tooling {
namespace internal {

inline llvm::Error findError() { return llvm::Error::success(); }

inline void ignoreError() {}

template <typename FirstT, typename... RestT>
void ignoreError(Expected<FirstT> &First, Expected<RestT> &... Rest) {
  if (!First)
    llvm::consumeError(First.takeError());
  ignoreError(Rest...);
}

/// Scans the tuple and returns a valid \c Error if any of the values are
/// invalid.
template <typename FirstT, typename... RestT>
llvm::Error findError(Expected<FirstT> &First, Expected<RestT> &... Rest) {
  if (!First) {
    ignoreError(Rest...);
    return First.takeError();
  }
  return findError(Rest...);
}

template <typename RuleType, typename... RequirementTypes, size_t... Is>
void invokeRuleAfterValidatingRequirements(
    RefactoringResultConsumer &Consumer, RefactoringRuleContext &Context,
    const std::tuple<RequirementTypes...> &Requirements,
    llvm::index_sequence<Is...>) {
  // Check if the requirements we're interested in can be evaluated.
  auto Values =
      std::make_tuple(std::get<Is>(Requirements).evaluate(Context)...);
  auto Err = findError(std::get<Is>(Values)...);
  if (Err)
    return Consumer.handleError(std::move(Err));
  // Construct the target action rule by extracting the evaluated
  // requirements from Expected<> wrappers and then run it.
  auto Rule =
      RuleType::initiate(Context, std::move((*std::get<Is>(Values)))...);
  if (!Rule)
    return Consumer.handleError(Rule.takeError());
  Rule->invoke(Consumer, Context);
}

inline void visitRefactoringOptionsImpl(RefactoringOptionVisitor &) {}

/// Scans the list of requirements in a rule and visits all the refactoring
/// options that are used by all the requirements.
template <typename FirstT, typename... RestT>
void visitRefactoringOptionsImpl(RefactoringOptionVisitor &Visitor,
                                 const FirstT &First, const RestT &... Rest) {
  struct OptionGatherer {
    RefactoringOptionVisitor &Visitor;

    void operator()(const RefactoringOptionsRequirement &Requirement) {
      for (const auto &Option : Requirement.getRefactoringOptions())
        Option->passToVisitor(Visitor);
    }
    void operator()(const RefactoringActionRuleRequirement &) {}
  };
  (OptionGatherer{Visitor})(First);
  return visitRefactoringOptionsImpl(Visitor, Rest...);
}

template <typename... RequirementTypes, size_t... Is>
void visitRefactoringOptions(
    RefactoringOptionVisitor &Visitor,
    const std::tuple<RequirementTypes...> &Requirements,
    llvm::index_sequence<Is...>) {
  visitRefactoringOptionsImpl(Visitor, std::get<Is>(Requirements)...);
}

/// A type trait that returns true when the given type list has at least one
/// type whose base is the given base type.
template <typename Base, typename First, typename... Rest>
struct HasBaseOf : std::conditional<HasBaseOf<Base, First>::value ||
                                        HasBaseOf<Base, Rest...>::value,
                                    std::true_type, std::false_type>::type {};

template <typename Base, typename T>
struct HasBaseOf<Base, T> : std::is_base_of<Base, T> {};

/// A type trait that returns true when the given type list contains types that
/// derive from Base.
template <typename Base, typename First, typename... Rest>
struct AreBaseOf : std::conditional<AreBaseOf<Base, First>::value &&
                                        AreBaseOf<Base, Rest...>::value,
                                    std::true_type, std::false_type>::type {};

template <typename Base, typename T>
struct AreBaseOf<Base, T> : std::is_base_of<Base, T> {};

} // end namespace internal

template <typename RuleType, typename... RequirementTypes>
std::unique_ptr<RefactoringActionRule>
createRefactoringActionRule(const RequirementTypes &... Requirements) {
  static_assert(std::is_base_of<RefactoringActionRuleBase, RuleType>::value,
                "Expected a refactoring action rule type");
  static_assert(internal::AreBaseOf<RefactoringActionRuleRequirement,
                                    RequirementTypes...>::value,
                "Expected a list of refactoring action rules");

  class Rule final : public RefactoringActionRule {
  public:
    Rule(std::tuple<RequirementTypes...> Requirements)
        : Requirements(Requirements) {}

    void invoke(RefactoringResultConsumer &Consumer,
                RefactoringRuleContext &Context) override {
      internal::invokeRuleAfterValidatingRequirements<RuleType>(
          Consumer, Context, Requirements,
          llvm::index_sequence_for<RequirementTypes...>());
    }

    bool hasSelectionRequirement() override {
      return internal::HasBaseOf<SourceSelectionRequirement,
                                 RequirementTypes...>::value;
    }

    void visitRefactoringOptions(RefactoringOptionVisitor &Visitor) override {
      internal::visitRefactoringOptions(
          Visitor, Requirements,
          llvm::index_sequence_for<RequirementTypes...>());
    }
  private:
    std::tuple<RequirementTypes...> Requirements;
  };

  return llvm::make_unique<Rule>(std::make_tuple(Requirements...));
}

} // end namespace tooling
} // end namespace clang

#endif // LLVM_CLANG_TOOLING_REFACTOR_REFACTORING_ACTION_RULES_INTERNAL_H