reference, declarationdefinition
definition → references, declarations, derived classes, virtual overrides
reference to multiple definitions → definitions
unreferenced
    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
//===--- RefactoringActionRulesInternal.h - Clang refactoring library -----===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//

#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,
    std::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,
    std::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,
          std::index_sequence_for<RequirementTypes...>());
    }

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

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

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

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

#endif // LLVM_CLANG_TOOLING_REFACTOR_REFACTORING_ACTION_RULES_INTERNAL_H