clang 20.0.0 (based on r547379) from build 12806354. Bug: http://b/379133546 Test: N/A Change-Id: I2eb8938af55d809de674be63cb30cf27e801862b Upstream-Commit: ad834e67b1105d15ef907f6255d4c96e8e733f57
213 lines
7.8 KiB
C++
213 lines
7.8 KiB
C++
//===--- 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_
|