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