Coverage Report

Created: 2022-05-17 06:19

/Users/buildslave/jenkins/workspace/coverage/llvm-project/clang/include/clang/Tooling/Transformer/Transformer.h
Line
Count
Source (jump to first uncovered line)
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
0
                    }) {}
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_