Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2026-05-10 08:37:12

0001 //===--- Transformer.h - Transformer class ----------------------*- C++ -*-===//
0002 //
0003 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
0004 // See https://llvm.org/LICENSE.txt for license information.
0005 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
0006 //
0007 //===----------------------------------------------------------------------===//
0008 
0009 #ifndef LLVM_CLANG_TOOLING_TRANSFORMER_TRANSFORMER_H_
0010 #define LLVM_CLANG_TOOLING_TRANSFORMER_TRANSFORMER_H_
0011 
0012 #include "clang/ASTMatchers/ASTMatchFinder.h"
0013 #include "clang/Tooling/Refactoring/AtomicChange.h"
0014 #include "clang/Tooling/Transformer/RewriteRule.h"
0015 #include "llvm/Support/Error.h"
0016 #include <functional>
0017 #include <utility>
0018 
0019 namespace clang {
0020 namespace tooling {
0021 
0022 namespace detail {
0023 /// Implementation details of \c Transformer with type erasure around
0024 /// \c RewriteRule<T> as well as the corresponding consumers.
0025 class TransformerImpl {
0026 public:
0027   virtual ~TransformerImpl() = default;
0028 
0029   void onMatch(const ast_matchers::MatchFinder::MatchResult &Result);
0030 
0031   virtual std::vector<ast_matchers::internal::DynTypedMatcher>
0032   buildMatchers() const = 0;
0033 
0034 protected:
0035   /// Converts a set of \c Edit into a \c AtomicChange per file modified.
0036   /// Returns an error if the edits fail to compose, e.g. overlapping edits.
0037   static llvm::Expected<llvm::SmallVector<AtomicChange, 1>>
0038   convertToAtomicChanges(const llvm::SmallVectorImpl<transformer::Edit> &Edits,
0039                          const ast_matchers::MatchFinder::MatchResult &Result);
0040 
0041 private:
0042   virtual void
0043   onMatchImpl(const ast_matchers::MatchFinder::MatchResult &Result) = 0;
0044 };
0045 
0046 // FIXME: Use std::type_identity or backport when available.
0047 template <class T> struct type_identity {
0048   using type = T;
0049 };
0050 } // namespace detail
0051 
0052 template <typename T> struct TransformerResult {
0053   llvm::MutableArrayRef<AtomicChange> Changes;
0054   T Metadata;
0055 };
0056 
0057 template <> struct TransformerResult<void> {
0058   llvm::MutableArrayRef<AtomicChange> Changes;
0059 };
0060 
0061 /// Handles the matcher and callback registration for a single `RewriteRule`, as
0062 /// defined by the arguments of the constructor.
0063 class Transformer : public ast_matchers::MatchFinder::MatchCallback {
0064 public:
0065   /// Provides the set of changes to the consumer.  The callback is free to move
0066   /// or destructively consume the changes as needed.
0067   ///
0068   /// We use \c MutableArrayRef as an abstraction to provide decoupling, and we
0069   /// expect the majority of consumers to copy or move the individual values
0070   /// into a separate data structure.
0071   using ChangeSetConsumer = std::function<void(
0072       Expected<llvm::MutableArrayRef<AtomicChange>> Changes)>;
0073 
0074   /// \param Consumer receives all rewrites for a single match, or an error.
0075   /// Will not necessarily be called for each match; for example, if the rule
0076   /// generates no edits but does not fail.  Note that clients are responsible
0077   /// for handling the case that independent \c AtomicChanges conflict with each
0078   /// other.
0079   explicit Transformer(transformer::RewriteRuleWith<void> Rule,
0080                        ChangeSetConsumer Consumer)
0081       : Transformer(std::move(Rule),
0082                     [Consumer = std::move(Consumer)](
0083                         llvm::Expected<TransformerResult<void>> Result) {
0084                       if (Result)
0085                         Consumer(Result->Changes);
0086                       else
0087                         Consumer(Result.takeError());
0088                     }) {}
0089 
0090   /// \param Consumer receives all rewrites and the associated metadata for a
0091   /// single match, or an error. Will always be called for each match, even if
0092   /// the rule generates no edits.  Note that clients are responsible for
0093   /// handling the case that independent \c AtomicChanges conflict with each
0094   /// other.
0095   template <typename MetadataT>
0096   explicit Transformer(
0097       transformer::RewriteRuleWith<MetadataT> Rule,
0098       std::function<void(llvm::Expected<TransformerResult<
0099                              typename detail::type_identity<MetadataT>::type>>)>
0100           Consumer);
0101 
0102   /// N.B. Passes `this` pointer to `MatchFinder`.  So, this object should not
0103   /// be moved after this call.
0104   void registerMatchers(ast_matchers::MatchFinder *MatchFinder);
0105 
0106   /// Not called directly by users -- called by the framework, via base class
0107   /// pointer.
0108   void run(const ast_matchers::MatchFinder::MatchResult &Result) override;
0109 
0110 private:
0111   std::unique_ptr<detail::TransformerImpl> Impl;
0112 };
0113 
0114 namespace detail {
0115 /// Runs the metadata generator on \c Rule and stuffs it into \c Result.
0116 /// @{
0117 template <typename T>
0118 llvm::Error
0119 populateMetadata(const transformer::RewriteRuleWith<T> &Rule,
0120                  size_t SelectedCase,
0121                  const ast_matchers::MatchFinder::MatchResult &Match,
0122                  TransformerResult<T> &Result) {
0123   // Silence a false positive GCC -Wunused-but-set-parameter warning in constexpr
0124   // cases, by marking SelectedCase as used. See
0125   // https://gcc.gnu.org/bugzilla/show_bug.cgi?id=85827 for details. The issue is
0126   // fixed in GCC 10.
0127   (void)SelectedCase;
0128   if constexpr (!std::is_void_v<T>) {
0129     auto Metadata = Rule.Metadata[SelectedCase]->eval(Match);
0130     if (!Metadata)
0131       return Metadata.takeError();
0132     Result.Metadata = std::move(*Metadata);
0133   }
0134   return llvm::Error::success();
0135 }
0136 /// @}
0137 
0138 /// Implementation when metadata is generated as a part of the rewrite. This
0139 /// happens when we have a \c RewriteRuleWith<T>.
0140 template <typename T> class WithMetadataImpl final : public TransformerImpl {
0141   transformer::RewriteRuleWith<T> Rule;
0142   std::function<void(llvm::Expected<TransformerResult<T>>)> Consumer;
0143 
0144 public:
0145   explicit WithMetadataImpl(
0146       transformer::RewriteRuleWith<T> R,
0147       std::function<void(llvm::Expected<TransformerResult<T>>)> Consumer)
0148       : Rule(std::move(R)), Consumer(std::move(Consumer)) {
0149     assert(llvm::all_of(Rule.Cases,
0150                         [](const transformer::RewriteRuleBase::Case &Case)
0151                             -> bool { return !!Case.Edits; }) &&
0152            "edit generator must be provided for each rule");
0153     if constexpr (!std::is_void_v<T>)
0154       assert(llvm::all_of(Rule.Metadata,
0155                           [](const typename transformer::Generator<T> &Metadata)
0156                               -> bool { return !!Metadata; }) &&
0157              "metadata generator must be provided for each rule");
0158   }
0159 
0160 private:
0161   void onMatchImpl(const ast_matchers::MatchFinder::MatchResult &Result) final {
0162     size_t I = transformer::detail::findSelectedCase(Result, Rule);
0163     auto Transformations = Rule.Cases[I].Edits(Result);
0164     if (!Transformations) {
0165       Consumer(Transformations.takeError());
0166       return;
0167     }
0168 
0169     llvm::SmallVector<AtomicChange, 1> Changes;
0170     if (!Transformations->empty()) {
0171       auto C = convertToAtomicChanges(*Transformations, Result);
0172       if (C) {
0173         Changes = std::move(*C);
0174       } else {
0175         Consumer(C.takeError());
0176         return;
0177       }
0178     } else if (std::is_void<T>::value) {
0179       // If we don't have metadata and we don't have any edits, skip.
0180       return;
0181     }
0182 
0183     TransformerResult<T> RewriteResult;
0184     if (auto E = populateMetadata(Rule, I, Result, RewriteResult)) {
0185       Consumer(std::move(E));
0186       return;
0187     }
0188 
0189     RewriteResult.Changes = llvm::MutableArrayRef<AtomicChange>(Changes);
0190     Consumer(std::move(RewriteResult));
0191   }
0192 
0193   std::vector<ast_matchers::internal::DynTypedMatcher>
0194   buildMatchers() const final {
0195     return transformer::detail::buildMatchers(Rule);
0196   }
0197 };
0198 } // namespace detail
0199 
0200 template <typename MetadataT>
0201 Transformer::Transformer(
0202     transformer::RewriteRuleWith<MetadataT> Rule,
0203     std::function<void(llvm::Expected<TransformerResult<
0204                            typename detail::type_identity<MetadataT>::type>>)>
0205         Consumer)
0206     : Impl(std::make_unique<detail::WithMetadataImpl<MetadataT>>(
0207           std::move(Rule), std::move(Consumer))) {}
0208 
0209 } // namespace tooling
0210 } // namespace clang
0211 
0212 #endif // LLVM_CLANG_TOOLING_TRANSFORMER_TRANSFORMER_H_