summaryrefslogtreecommitdiff
path: root/include/clang/Tooling/Transformer/Stencil.h
blob: 617585cacdbfb5ac62ad499fa261deb180b3ff26 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
//===--- Stencil.h - Stencil class ------------------------------*- C++ -*-===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
///
/// \file
/// This file defines the *Stencil* abstraction: a code-generating object,
/// parameterized by named references to (bound) AST nodes.  Given a match
/// result, a stencil can be evaluated to a string of source code.
///
/// A stencil is similar in spirit to a format string: it is composed of a
/// series of raw text strings, references to nodes (the parameters) and helper
/// code-generation operations.
///
//===----------------------------------------------------------------------===//

#ifndef LLVM_CLANG_TOOLING_TRANSFORMER_STENCIL_H_
#define LLVM_CLANG_TOOLING_TRANSFORMER_STENCIL_H_

#include "clang/AST/ASTContext.h"
#include "clang/AST/ASTTypeTraits.h"
#include "clang/ASTMatchers/ASTMatchFinder.h"
#include "clang/Tooling/Transformer/MatchConsumer.h"
#include "clang/Tooling/Transformer/RangeSelector.h"
#include "llvm/ADT/StringRef.h"
#include "llvm/Support/Error.h"
#include <string>
#include <vector>

namespace clang {
namespace tooling {

/// A stencil is represented as a sequence of "parts" that can each individually
/// generate a code string based on a match result.  The different kinds of
/// parts include (raw) text, references to bound nodes and assorted operations
/// on bound nodes.
///
/// Users can create custom Stencil operations by implementing this interface.
class StencilPartInterface {
public:
  virtual ~StencilPartInterface() = default;

  /// Evaluates this part to a string and appends it to \c Result.  \c Result is
  /// undefined in the case of an error.
  virtual llvm::Error eval(const ast_matchers::MatchFinder::MatchResult &Match,
                           std::string *Result) const = 0;

  virtual bool isEqual(const StencilPartInterface &other) const = 0;

  /// Constructs a string representation of the StencilPart. StencilParts
  /// generated by the `selection` and `run` functions do not have a unique
  /// string representation.
  virtual std::string toString() const = 0;

  const void *typeId() const { return TypeId; }

protected:
  StencilPartInterface(const void *DerivedId) : TypeId(DerivedId) {}

  // Since this is an abstract class, copying/assigning only make sense for
  // derived classes implementing `clone()`.
  StencilPartInterface(const StencilPartInterface &) = default;
  StencilPartInterface &operator=(const StencilPartInterface &) = default;

  /// Unique identifier of the concrete type of this instance.  Supports safe
  /// downcasting.
  const void *TypeId;
};

/// A copyable facade for a std::unique_ptr<StencilPartInterface>. Copies result
/// in a copy of the underlying pointee object.
class StencilPart {
public:
  explicit StencilPart(std::shared_ptr<StencilPartInterface> Impl)
      : Impl(std::move(Impl)) {}

  /// See `StencilPartInterface::eval()`.
  llvm::Error eval(const ast_matchers::MatchFinder::MatchResult &Match,
                   std::string *Result) const {
    return Impl->eval(Match, Result);
  }

  bool operator==(const StencilPart &Other) const {
    if (Impl == Other.Impl)
      return true;
    if (Impl == nullptr || Other.Impl == nullptr)
      return false;
    return Impl->isEqual(*Other.Impl);
  }

  std::string toString() const {
    if (Impl == nullptr)
      return "";
    return Impl->toString();
  }

private:
  std::shared_ptr<StencilPartInterface> Impl;
};

/// A sequence of code fragments, references to parameters and code-generation
/// operations that together can be evaluated to (a fragment of) source code,
/// given a match result.
class Stencil {
public:
  Stencil() = default;

  /// Composes a stencil from a series of parts.
  template <typename... Ts> static Stencil cat(Ts &&... Parts) {
    Stencil S;
    S.Parts = {wrap(std::forward<Ts>(Parts))...};
    return S;
  }

  /// Appends data from a \p OtherStencil to this stencil.
  void append(Stencil OtherStencil);

  // Evaluates the stencil given a match result. Requires that the nodes in the
  // result includes any ids referenced in the stencil. References to missing
  // nodes will result in an invalid_argument error.
  llvm::Expected<std::string>
  eval(const ast_matchers::MatchFinder::MatchResult &Match) const;

  // Allow Stencils to operate as std::function, for compatibility with
  // Transformer's TextGenerator.
  llvm::Expected<std::string>
  operator()(const ast_matchers::MatchFinder::MatchResult &Result) const {
    return eval(Result);
  }

  /// Constructs a string representation of the Stencil. The string is not
  /// guaranteed to be unique.
  std::string toString() const {
    std::vector<std::string> PartStrings;
    PartStrings.reserve(Parts.size());
    for (const auto &Part : Parts)
      PartStrings.push_back(Part.toString());
    return llvm::join(PartStrings, ", ");
  }

private:
  friend bool operator==(const Stencil &A, const Stencil &B);
  static StencilPart wrap(llvm::StringRef Text);
  static StencilPart wrap(RangeSelector Selector);
  static StencilPart wrap(StencilPart Part) { return Part; }

  std::vector<StencilPart> Parts;
};

inline bool operator==(const Stencil &A, const Stencil &B) {
  return A.Parts == B.Parts;
}

inline bool operator!=(const Stencil &A, const Stencil &B) { return !(A == B); }

// Functions for conveniently building stencils.
namespace stencil {
/// Convenience wrapper for Stencil::cat that can be imported with a using decl.
template <typename... Ts> Stencil cat(Ts &&... Parts) {
  return Stencil::cat(std::forward<Ts>(Parts)...);
}

/// \returns exactly the text provided.
StencilPart text(llvm::StringRef Text);

/// \returns the source corresponding to the selected range.
StencilPart selection(RangeSelector Selector);

/// \returns the source corresponding to the identified node.
/// FIXME: Deprecated. Write `selection(node(Id))` instead.
inline StencilPart node(llvm::StringRef Id) {
  return selection(tooling::node(Id));
}

/// Generates the source of the expression bound to \p Id, wrapping it in
/// parentheses if it may parse differently depending on context. For example, a
/// binary operation is always wrapped, while a variable reference is never
/// wrapped.
StencilPart expression(llvm::StringRef Id);

/// Constructs an idiomatic dereferencing of the expression bound to \p ExprId.
/// \p ExprId is wrapped in parentheses, if needed.
StencilPart deref(llvm::StringRef ExprId);

/// Constructs an expression that idiomatically takes the address of the
/// expression bound to \p ExprId. \p ExprId is wrapped in parentheses, if
/// needed.
StencilPart addressOf(llvm::StringRef ExprId);

/// Constructs a `MemberExpr` that accesses the named member (\p Member) of the
/// object bound to \p BaseId. The access is constructed idiomatically: if \p
/// BaseId is bound to `e` and \p Member identifies member `m`, then returns
/// `e->m`, when e is a pointer, `e2->m` when e = `*e2` and `e.m` otherwise.
/// Additionally, `e` is wrapped in parentheses, if needed.
StencilPart access(llvm::StringRef BaseId, StencilPart Member);
inline StencilPart access(llvm::StringRef BaseId, llvm::StringRef Member) {
  return access(BaseId, text(Member));
}

/// Chooses between the two stencil parts, based on whether \p ID is bound in
/// the match.
StencilPart ifBound(llvm::StringRef Id, StencilPart TruePart,
                    StencilPart FalsePart);

/// Chooses between the two strings, based on whether \p ID is bound in the
/// match.
inline StencilPart ifBound(llvm::StringRef Id, llvm::StringRef TrueText,
                           llvm::StringRef FalseText) {
  return ifBound(Id, text(TrueText), text(FalseText));
}

/// Wraps a MatchConsumer in a StencilPart, so that it can be used in a Stencil.
/// This supports user-defined extensions to the Stencil language.
StencilPart run(MatchConsumer<std::string> C);

/// For debug use only; semantics are not guaranteed.
///
/// \returns the string resulting from calling the node's print() method.
StencilPart dPrint(llvm::StringRef Id);
} // namespace stencil
} // namespace tooling
} // namespace clang
#endif // LLVM_CLANG_TOOLING_TRANSFORMER_STENCIL_H_