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