#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 {
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:
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;
};
template <class T> struct type_identity {
using type = T;
};
}
template <typename T> struct TransformerResult {
llvm::MutableArrayRef<AtomicChange> Changes;
T Metadata;
};
template <> struct TransformerResult<void> {
llvm::MutableArrayRef<AtomicChange> Changes;
};
class Transformer : public ast_matchers::MatchFinder::MatchCallback {
public:
using ChangeSetConsumer = std::function<void(
Expected<llvm::MutableArrayRef<AtomicChange>> Changes)>;
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());
}) {}
template <typename MetadataT>
explicit Transformer(
transformer::RewriteRuleWith<MetadataT> Rule,
std::function<void(llvm::Expected<TransformerResult<
typename detail::type_identity<MetadataT>::type>>)>
Consumer);
void registerMatchers(ast_matchers::MatchFinder *MatchFinder);
void run(const ast_matchers::MatchFinder::MatchResult &Result) override;
private:
std::unique_ptr<detail::TransformerImpl> Impl;
};
namespace detail {
template <typename T>
void assertMetadataSet(const transformer::RewriteRuleWith<T> &Rule) {
assert(llvm::all_of(Rule.Metadata,
[](const typename transformer::Generator<T> &Metadata)
-> bool { return !!Metadata; }) &&
"metadata generator must be provided for each rule");
}
template <>
inline void assertMetadataSet(const transformer::RewriteRuleWith<void> &) {}
template <typename T>
llvm::Error
populateMetadata(const transformer::RewriteRuleWith<T> &Rule,
size_t SelectedCase,
const ast_matchers::MatchFinder::MatchResult &Match,
TransformerResult<T> &Result) {
auto Metadata = Rule.Metadata[SelectedCase]->eval(Match);
if (!Metadata)
return Metadata.takeError();
Result.Metadata = std::move(*Metadata);
return llvm::Error::success();
}
template <>
inline llvm::Error
populateMetadata(const transformer::RewriteRuleWith<void> &, size_t,
const ast_matchers::MatchFinder::MatchResult &Match,
TransformerResult<void> &) {
return llvm::Error::success();
}
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");
assertMetadataSet(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) {
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);
}
};
}
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))) {}
} }
#endif