//===--- Transformer.h - Transformer class ----------------------*- C++ -*-===//
 
//
 
// 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_TRANSFORMER_TRANSFORMER_H_
 
#define LLVM_CLANG_TOOLING_TRANSFORMER_TRANSFORMER_H_
 
 
 
#include "clang/ASTMatchers/ASTMatchFinder.h"
 
#include "clang/Tooling/Refactoring/AtomicChange.h"
 
#include "clang/Tooling/Transformer/RewriteRule.h"
 
#include "llvm/Support/Error.h"
 
#include <functional>
 
#include <utility>
 
 
 
namespace clang {
 
namespace tooling {
 
 
 
namespace detail {
 
/// Implementation details of \c Transformer with type erasure around
 
/// \c RewriteRule<T> as well as the corresponding consumers.
 
class TransformerImpl {
 
public:
 
  virtual ~TransformerImpl() = default;
 
 
 
  void onMatch(const ast_matchers::MatchFinder::MatchResult &Result);
 
 
 
  virtual std::vector<ast_matchers::internal::DynTypedMatcher>
 
  buildMatchers() const = 0;
 
 
 
protected:
 
  /// Converts a set of \c Edit into a \c AtomicChange per file modified.
 
  /// Returns an error if the edits fail to compose, e.g. overlapping edits.
 
  static llvm::Expected<llvm::SmallVector<AtomicChange, 1>>
 
  convertToAtomicChanges(const llvm::SmallVectorImpl<transformer::Edit> &Edits,
 
                         const ast_matchers::MatchFinder::MatchResult &Result);
 
 
 
private:
 
  virtual void
 
  onMatchImpl(const ast_matchers::MatchFinder::MatchResult &Result) = 0;
 
};
 
 
 
// FIXME: Use std::type_identity or backport when available.
 
template <class T> struct type_identity {
 
  using type = T;
 
};
 
} // namespace detail
 
 
 
template <typename T> struct TransformerResult {
 
  llvm::MutableArrayRef<AtomicChange> Changes;
 
  T Metadata;
 
};
 
 
 
template <> struct TransformerResult<void> {
 
  llvm::MutableArrayRef<AtomicChange> Changes;
 
};
 
 
 
/// Handles the matcher and callback registration for a single `RewriteRule`, as
 
/// defined by the arguments of the constructor.
 
class Transformer : public ast_matchers::MatchFinder::MatchCallback {
 
public:
 
  /// Provides the set of changes to the consumer.  The callback is free to move
 
  /// or destructively consume the changes as needed.
 
  ///
 
  /// We use \c MutableArrayRef as an abstraction to provide decoupling, and we
 
  /// expect the majority of consumers to copy or move the individual values
 
  /// into a separate data structure.
 
  using ChangeSetConsumer = std::function<void(
 
      Expected<llvm::MutableArrayRef<AtomicChange>> Changes)>;
 
 
 
  /// \param Consumer receives all rewrites for a single match, or an error.
 
  /// Will not necessarily be called for each match; for example, if the rule
 
  /// generates no edits but does not fail.  Note that clients are responsible
 
  /// for handling the case that independent \c AtomicChanges conflict with each
 
  /// other.
 
  explicit Transformer(transformer::RewriteRuleWith<void> Rule,
 
                       ChangeSetConsumer Consumer)
 
      : Transformer(std::move(Rule),
 
                    [Consumer = std::move(Consumer)](
 
                        llvm::Expected<TransformerResult<void>> Result) {
 
                      if (Result)
 
                        Consumer(Result->Changes);
 
                      else
 
                        Consumer(Result.takeError());
 
                    }) {}
 
 
 
  /// \param Consumer receives all rewrites and the associated metadata for a
 
  /// single match, or an error. Will always be called for each match, even if
 
  /// the rule generates no edits.  Note that clients are responsible for
 
  /// handling the case that independent \c AtomicChanges conflict with each
 
  /// other.
 
  template <typename MetadataT>
 
  explicit Transformer(
 
      transformer::RewriteRuleWith<MetadataT> Rule,
 
      std::function<void(llvm::Expected<TransformerResult<
 
                             typename detail::type_identity<MetadataT>::type>>)>
 
          Consumer);
 
 
 
  /// N.B. Passes `this` pointer to `MatchFinder`.  So, this object should not
 
  /// be moved after this call.
 
  void registerMatchers(ast_matchers::MatchFinder *MatchFinder);
 
 
 
  /// Not called directly by users -- called by the framework, via base class
 
  /// pointer.
 
  void run(const ast_matchers::MatchFinder::MatchResult &Result) override;
 
 
 
private:
 
  std::unique_ptr<detail::TransformerImpl> Impl;
 
};
 
 
 
namespace detail {
 
/// Runs the metadata generator on \c Rule and stuffs it into \c Result.
 
/// @{
 
template <typename T>
 
llvm::Error
 
populateMetadata(const transformer::RewriteRuleWith<T> &Rule,
 
                 size_t SelectedCase,
 
                 const ast_matchers::MatchFinder::MatchResult &Match,
 
                 TransformerResult<T> &Result) {
 
  // Silence a false positive GCC -Wunused-but-set-parameter warning in constexpr
 
  // cases, by marking SelectedCase as used. See
 
  // https://gcc.gnu.org/bugzilla/show_bug.cgi?id=85827 for details. The issue is
 
  // fixed in GCC 10.
 
  (void)SelectedCase;
 
  if constexpr (!std::is_void_v<T>) {
 
    auto Metadata = Rule.Metadata[SelectedCase]->eval(Match);
 
    if (!Metadata)
 
      return Metadata.takeError();
 
    Result.Metadata = std::move(*Metadata);
 
  }
 
  return llvm::Error::success();
 
}
 
/// @}
 
 
 
/// Implementation when metadata is generated as a part of the rewrite. This
 
/// happens when we have a \c RewriteRuleWith<T>.
 
template <typename T> class WithMetadataImpl final : public TransformerImpl {
 
  transformer::RewriteRuleWith<T> Rule;
 
  std::function<void(llvm::Expected<TransformerResult<T>>)> Consumer;
 
 
 
public:
 
  explicit WithMetadataImpl(
 
      transformer::RewriteRuleWith<T> R,
 
      std::function<void(llvm::Expected<TransformerResult<T>>)> Consumer)
 
      : Rule(std::move(R)), Consumer(std::move(Consumer)) {
 
    assert(llvm::all_of(Rule.Cases,
 
                        [](const transformer::RewriteRuleBase::Case &Case)
 
                            -> bool { return !!Case.Edits; }) &&
 
           "edit generator must be provided for each rule");
 
    if constexpr (!std::is_void_v<T>)
 
      assert(llvm::all_of(Rule.Metadata,
 
                          [](const typename transformer::Generator<T> &Metadata)
 
                              -> bool { return !!Metadata; }) &&
 
             "metadata generator must be provided for each rule");
 
  }
 
 
 
private:
 
  void onMatchImpl(const ast_matchers::MatchFinder::MatchResult &Result) final {
 
    size_t I = transformer::detail::findSelectedCase(Result, Rule);
 
    auto Transformations = Rule.Cases[I].Edits(Result);
 
    if (!Transformations) {
 
      Consumer(Transformations.takeError());
 
      return;
 
    }
 
 
 
    llvm::SmallVector<AtomicChange, 1> Changes;
 
    if (!Transformations->empty()) {
 
      auto C = convertToAtomicChanges(*Transformations, Result);
 
      if (C) {
 
        Changes = std::move(*C);
 
      } else {
 
        Consumer(C.takeError());
 
        return;
 
      }
 
    } else if (std::is_void<T>::value) {
 
      // If we don't have metadata and we don't have any edits, skip.
 
      return;
 
    }
 
 
 
    TransformerResult<T> RewriteResult;
 
    if (auto E = populateMetadata(Rule, I, Result, RewriteResult)) {
 
      Consumer(std::move(E));
 
      return;
 
    }
 
 
 
    RewriteResult.Changes = llvm::MutableArrayRef<AtomicChange>(Changes);
 
    Consumer(std::move(RewriteResult));
 
  }
 
 
 
  std::vector<ast_matchers::internal::DynTypedMatcher>
 
  buildMatchers() const final {
 
    return transformer::detail::buildMatchers(Rule);
 
  }
 
};
 
} // namespace detail
 
 
 
template <typename MetadataT>
 
Transformer::Transformer(
 
    transformer::RewriteRuleWith<MetadataT> Rule,
 
    std::function<void(llvm::Expected<TransformerResult<
 
                           typename detail::type_identity<MetadataT>::type>>)>
 
        Consumer)
 
    : Impl(std::make_unique<detail::WithMetadataImpl<MetadataT>>(
 
          std::move(Rule), std::move(Consumer))) {}
 
 
 
} // namespace tooling
 
} // namespace clang
 
 
 
#endif // LLVM_CLANG_TOOLING_TRANSFORMER_TRANSFORMER_H_