Subversion Repositories QNX 8.QNX8 LLVM/Clang compiler suite

Rev

Blame | Last modification | View Log | Download | RSS feed

  1. //===--- Transformer.h - Transformer class ----------------------*- C++ -*-===//
  2. //
  3. // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
  4. // See https://llvm.org/LICENSE.txt for license information.
  5. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
  6. //
  7. //===----------------------------------------------------------------------===//
  8.  
  9. #ifndef LLVM_CLANG_TOOLING_TRANSFORMER_TRANSFORMER_H_
  10. #define LLVM_CLANG_TOOLING_TRANSFORMER_TRANSFORMER_H_
  11.  
  12. #include "clang/ASTMatchers/ASTMatchFinder.h"
  13. #include "clang/Tooling/Refactoring/AtomicChange.h"
  14. #include "clang/Tooling/Transformer/RewriteRule.h"
  15. #include "llvm/Support/Error.h"
  16. #include <functional>
  17. #include <utility>
  18.  
  19. namespace clang {
  20. namespace tooling {
  21.  
  22. namespace detail {
  23. /// Implementation details of \c Transformer with type erasure around
  24. /// \c RewriteRule<T> as well as the corresponding consumers.
  25. class TransformerImpl {
  26. public:
  27.   virtual ~TransformerImpl() = default;
  28.  
  29.   void onMatch(const ast_matchers::MatchFinder::MatchResult &Result);
  30.  
  31.   virtual std::vector<ast_matchers::internal::DynTypedMatcher>
  32.   buildMatchers() const = 0;
  33.  
  34. protected:
  35.   /// Converts a set of \c Edit into a \c AtomicChange per file modified.
  36.   /// Returns an error if the edits fail to compose, e.g. overlapping edits.
  37.   static llvm::Expected<llvm::SmallVector<AtomicChange, 1>>
  38.   convertToAtomicChanges(const llvm::SmallVectorImpl<transformer::Edit> &Edits,
  39.                          const ast_matchers::MatchFinder::MatchResult &Result);
  40.  
  41. private:
  42.   virtual void
  43.   onMatchImpl(const ast_matchers::MatchFinder::MatchResult &Result) = 0;
  44. };
  45.  
  46. // FIXME: Use std::type_identity or backport when available.
  47. template <class T> struct type_identity {
  48.   using type = T;
  49. };
  50. } // namespace detail
  51.  
  52. template <typename T> struct TransformerResult {
  53.   llvm::MutableArrayRef<AtomicChange> Changes;
  54.   T Metadata;
  55. };
  56.  
  57. template <> struct TransformerResult<void> {
  58.   llvm::MutableArrayRef<AtomicChange> Changes;
  59. };
  60.  
  61. /// Handles the matcher and callback registration for a single `RewriteRule`, as
  62. /// defined by the arguments of the constructor.
  63. class Transformer : public ast_matchers::MatchFinder::MatchCallback {
  64. public:
  65.   /// Provides the set of changes to the consumer.  The callback is free to move
  66.   /// or destructively consume the changes as needed.
  67.   ///
  68.   /// We use \c MutableArrayRef as an abstraction to provide decoupling, and we
  69.   /// expect the majority of consumers to copy or move the individual values
  70.   /// into a separate data structure.
  71.   using ChangeSetConsumer = std::function<void(
  72.       Expected<llvm::MutableArrayRef<AtomicChange>> Changes)>;
  73.  
  74.   /// \param Consumer receives all rewrites for a single match, or an error.
  75.   /// Will not necessarily be called for each match; for example, if the rule
  76.   /// generates no edits but does not fail.  Note that clients are responsible
  77.   /// for handling the case that independent \c AtomicChanges conflict with each
  78.   /// other.
  79.   explicit Transformer(transformer::RewriteRuleWith<void> Rule,
  80.                        ChangeSetConsumer Consumer)
  81.       : Transformer(std::move(Rule),
  82.                     [Consumer = std::move(Consumer)](
  83.                         llvm::Expected<TransformerResult<void>> Result) {
  84.                       if (Result)
  85.                         Consumer(Result->Changes);
  86.                       else
  87.                         Consumer(Result.takeError());
  88.                     }) {}
  89.  
  90.   /// \param Consumer receives all rewrites and the associated metadata for a
  91.   /// single match, or an error. Will always be called for each match, even if
  92.   /// the rule generates no edits.  Note that clients are responsible for
  93.   /// handling the case that independent \c AtomicChanges conflict with each
  94.   /// other.
  95.   template <typename MetadataT>
  96.   explicit Transformer(
  97.       transformer::RewriteRuleWith<MetadataT> Rule,
  98.       std::function<void(llvm::Expected<TransformerResult<
  99.                              typename detail::type_identity<MetadataT>::type>>)>
  100.           Consumer);
  101.  
  102.   /// N.B. Passes `this` pointer to `MatchFinder`.  So, this object should not
  103.   /// be moved after this call.
  104.   void registerMatchers(ast_matchers::MatchFinder *MatchFinder);
  105.  
  106.   /// Not called directly by users -- called by the framework, via base class
  107.   /// pointer.
  108.   void run(const ast_matchers::MatchFinder::MatchResult &Result) override;
  109.  
  110. private:
  111.   std::unique_ptr<detail::TransformerImpl> Impl;
  112. };
  113.  
  114. namespace detail {
  115. /// Runs the metadata generator on \c Rule and stuffs it into \c Result.
  116. /// @{
  117. template <typename T>
  118. llvm::Error
  119. populateMetadata(const transformer::RewriteRuleWith<T> &Rule,
  120.                  size_t SelectedCase,
  121.                  const ast_matchers::MatchFinder::MatchResult &Match,
  122.                  TransformerResult<T> &Result) {
  123.   // Silence a false positive GCC -Wunused-but-set-parameter warning in constexpr
  124.   // cases, by marking SelectedCase as used. See
  125.   // https://gcc.gnu.org/bugzilla/show_bug.cgi?id=85827 for details. The issue is
  126.   // fixed in GCC 10.
  127.   (void)SelectedCase;
  128.   if constexpr (!std::is_void_v<T>) {
  129.     auto Metadata = Rule.Metadata[SelectedCase]->eval(Match);
  130.     if (!Metadata)
  131.       return Metadata.takeError();
  132.     Result.Metadata = std::move(*Metadata);
  133.   }
  134.   return llvm::Error::success();
  135. }
  136. /// @}
  137.  
  138. /// Implementation when metadata is generated as a part of the rewrite. This
  139. /// happens when we have a \c RewriteRuleWith<T>.
  140. template <typename T> class WithMetadataImpl final : public TransformerImpl {
  141.   transformer::RewriteRuleWith<T> Rule;
  142.   std::function<void(llvm::Expected<TransformerResult<T>>)> Consumer;
  143.  
  144. public:
  145.   explicit WithMetadataImpl(
  146.       transformer::RewriteRuleWith<T> R,
  147.       std::function<void(llvm::Expected<TransformerResult<T>>)> Consumer)
  148.       : Rule(std::move(R)), Consumer(std::move(Consumer)) {
  149.     assert(llvm::all_of(Rule.Cases,
  150.                         [](const transformer::RewriteRuleBase::Case &Case)
  151.                             -> bool { return !!Case.Edits; }) &&
  152.            "edit generator must be provided for each rule");
  153.     if constexpr (!std::is_void_v<T>)
  154.       assert(llvm::all_of(Rule.Metadata,
  155.                           [](const typename transformer::Generator<T> &Metadata)
  156.                               -> bool { return !!Metadata; }) &&
  157.              "metadata generator must be provided for each rule");
  158.   }
  159.  
  160. private:
  161.   void onMatchImpl(const ast_matchers::MatchFinder::MatchResult &Result) final {
  162.     size_t I = transformer::detail::findSelectedCase(Result, Rule);
  163.     auto Transformations = Rule.Cases[I].Edits(Result);
  164.     if (!Transformations) {
  165.       Consumer(Transformations.takeError());
  166.       return;
  167.     }
  168.  
  169.     llvm::SmallVector<AtomicChange, 1> Changes;
  170.     if (!Transformations->empty()) {
  171.       auto C = convertToAtomicChanges(*Transformations, Result);
  172.       if (C) {
  173.         Changes = std::move(*C);
  174.       } else {
  175.         Consumer(C.takeError());
  176.         return;
  177.       }
  178.     } else if (std::is_void<T>::value) {
  179.       // If we don't have metadata and we don't have any edits, skip.
  180.       return;
  181.     }
  182.  
  183.     TransformerResult<T> RewriteResult;
  184.     if (auto E = populateMetadata(Rule, I, Result, RewriteResult)) {
  185.       Consumer(std::move(E));
  186.       return;
  187.     }
  188.  
  189.     RewriteResult.Changes = llvm::MutableArrayRef<AtomicChange>(Changes);
  190.     Consumer(std::move(RewriteResult));
  191.   }
  192.  
  193.   std::vector<ast_matchers::internal::DynTypedMatcher>
  194.   buildMatchers() const final {
  195.     return transformer::detail::buildMatchers(Rule);
  196.   }
  197. };
  198. } // namespace detail
  199.  
  200. template <typename MetadataT>
  201. Transformer::Transformer(
  202.     transformer::RewriteRuleWith<MetadataT> Rule,
  203.     std::function<void(llvm::Expected<TransformerResult<
  204.                            typename detail::type_identity<MetadataT>::type>>)>
  205.         Consumer)
  206.     : Impl(std::make_unique<detail::WithMetadataImpl<MetadataT>>(
  207.           std::move(Rule), std::move(Consumer))) {}
  208.  
  209. } // namespace tooling
  210. } // namespace clang
  211.  
  212. #endif // LLVM_CLANG_TOOLING_TRANSFORMER_TRANSFORMER_H_
  213.