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 /// Asserts that all \c Metadata for the \c Rule is set.
116 /// FIXME: Use constexpr-if in C++17.
117 /// @{
118 template <typename T>
119 void assertMetadataSet(const transformer::RewriteRuleWith<T> &Rule) {
120   assert(llvm::all_of(Rule.Metadata,
121                       [](const typename transformer::Generator<T> &Metadata)
122                           -> bool { return !!Metadata; }) &&
123          "metadata generator must be provided for each rule");
124 }
125 template <>
126 inline void assertMetadataSet(const transformer::RewriteRuleWith<void> &) {}
127 /// @}
128 
129 /// Runs the metadata generator on \c Rule and stuffs it into \c Result.
130 /// FIXME: Use constexpr-if in C++17.
131 /// @{
132 template <typename T>
133 llvm::Error
134 populateMetadata(const transformer::RewriteRuleWith<T> &Rule,
135                  size_t SelectedCase,
136                  const ast_matchers::MatchFinder::MatchResult &Match,
137                  TransformerResult<T> &Result) {
138   auto Metadata = Rule.Metadata[SelectedCase]->eval(Match);
139   if (!Metadata)
140     return Metadata.takeError();
141   Result.Metadata = std::move(*Metadata);
142   return llvm::Error::success();
143 }
144 template <>
145 inline llvm::Error
146 populateMetadata(const transformer::RewriteRuleWith<void> &, size_t,
147                  const ast_matchers::MatchFinder::MatchResult &Match,
148                  TransformerResult<void> &) {
149   return llvm::Error::success();
150 }
151 /// @}
152 
153 /// Implementation when metadata is generated as a part of the rewrite. This
154 /// happens when we have a \c RewriteRuleWith<T>.
155 template <typename T> class WithMetadataImpl final : public TransformerImpl {
156   transformer::RewriteRuleWith<T> Rule;
157   std::function<void(llvm::Expected<TransformerResult<T>>)> Consumer;
158 
159 public:
160   explicit WithMetadataImpl(
161       transformer::RewriteRuleWith<T> R,
162       std::function<void(llvm::Expected<TransformerResult<T>>)> Consumer)
163       : Rule(std::move(R)), Consumer(std::move(Consumer)) {
164     assert(llvm::all_of(Rule.Cases,
165                         [](const transformer::RewriteRuleBase::Case &Case)
166                             -> bool { return !!Case.Edits; }) &&
167            "edit generator must be provided for each rule");
168     assertMetadataSet(Rule);
169   }
170 
171 private:
172   void onMatchImpl(const ast_matchers::MatchFinder::MatchResult &Result) final {
173     size_t I = transformer::detail::findSelectedCase(Result, Rule);
174     auto Transformations = Rule.Cases[I].Edits(Result);
175     if (!Transformations) {
176       Consumer(Transformations.takeError());
177       return;
178     }
179 
180     llvm::SmallVector<AtomicChange, 1> Changes;
181     if (!Transformations->empty()) {
182       auto C = convertToAtomicChanges(*Transformations, Result);
183       if (C) {
184         Changes = std::move(*C);
185       } else {
186         Consumer(C.takeError());
187         return;
188       }
189     } else if (std::is_void<T>::value) {
190       // If we don't have metadata and we don't have any edits, skip.
191       return;
192     }
193 
194     TransformerResult<T> RewriteResult;
195     if (auto E = populateMetadata(Rule, I, Result, RewriteResult)) {
196       Consumer(std::move(E));
197       return;
198     }
199 
200     RewriteResult.Changes = llvm::MutableArrayRef<AtomicChange>(Changes);
201     Consumer(std::move(RewriteResult));
202   }
203 
204   std::vector<ast_matchers::internal::DynTypedMatcher>
205   buildMatchers() const final {
206     return transformer::detail::buildMatchers(Rule);
207   }
208 };
209 } // namespace detail
210 
211 template <typename MetadataT>
212 Transformer::Transformer(
213     transformer::RewriteRuleWith<MetadataT> Rule,
214     std::function<void(llvm::Expected<TransformerResult<
215                            typename detail::type_identity<MetadataT>::type>>)>
216         Consumer)
217     : Impl(std::make_unique<detail::WithMetadataImpl<MetadataT>>(
218           std::move(Rule), std::move(Consumer))) {}
219 
220 } // namespace tooling
221 } // namespace clang
222 
223 #endif // LLVM_CLANG_TOOLING_TRANSFORMER_TRANSFORMER_H_
224