Skip to content

Commit ce2b5cb

Browse files
committed
[libTooling] Simplify type structure of Stencils.
Summary: Currently, stencils are defined as a sequence of `StencilParts`. This differentiation adds an unneeded layer of complexity to the definition of Stencils. This change significantly simplifies the type structure: a stencil is now conceptually any object implementing `StencilInterface` and `Stencil` is just a thin wrapper for pointers to this interface. To account for the sequencing that was supported by the old `Stencil` type, we introduce a sequencing class that implements `StencilInterface`. That is, sequences are just another kind of Stencil and no longer have any special status. Corresponding to this change in the type structure, we change the way `cat` is used (and defined). `cat` bundles multiple features: it builds a stencil from a sequence of subcomponents and admits multiple different types for its arguments, while coercing them into the right type. Previously, `cat` was also used to coerce a single `StencilPart` into a `Stencil`. With that distinction gone, many uses of `cat` (e.g. in the tests) are unnecessary and have, therefore, been removed. Reviewers: gribozavr Subscribers: cfe-commits Tags: #clang Differential Revision: https://reviews.llvm.org/D69613
1 parent e1f8c8a commit ce2b5cb

File tree

3 files changed

+221
-239
lines changed

3 files changed

+221
-239
lines changed

clang/include/clang/Tooling/Transformer/Stencil.h

Lines changed: 62 additions & 127 deletions
Original file line numberDiff line numberDiff line change
@@ -32,197 +32,132 @@
3232

3333
namespace clang {
3434
namespace transformer {
35-
/// A stencil is represented as a sequence of "parts" that can each individually
36-
/// generate a code string based on a match result. The different kinds of
37-
/// parts include (raw) text, references to bound nodes and assorted operations
38-
/// on bound nodes.
39-
///
40-
/// Users can create custom Stencil operations by implementing this interface.
41-
class StencilPartInterface {
35+
class StencilInterface {
4236
public:
43-
virtual ~StencilPartInterface() = default;
37+
virtual ~StencilInterface() = default;
4438

4539
/// Evaluates this part to a string and appends it to \c Result. \c Result is
46-
/// undefined in the case of an error.
40+
/// undefined in the case of an error. `Result` is an out parameter to
41+
/// optimize the expected common case of evaluating a sequence of generators.
4742
virtual llvm::Error eval(const ast_matchers::MatchFinder::MatchResult &Match,
4843
std::string *Result) const = 0;
4944

50-
/// Constructs a string representation of the StencilPart. StencilParts
51-
/// generated by the `selection` and `run` functions do not have a unique
52-
/// string representation.
45+
/// Convenience version of `eval`, for the case where the generator is being
46+
/// evaluated on its own.
47+
llvm::Expected<std::string>
48+
eval(const ast_matchers::MatchFinder::MatchResult &R) const;
49+
50+
/// Constructs a string representation of the StencilInterface. The
51+
/// representation must be deterministic, but is not required to be unique.
5352
virtual std::string toString() const = 0;
5453

5554
protected:
56-
StencilPartInterface() = default;
55+
StencilInterface() = default;
5756

5857
// Since this is an abstract class, copying/assigning only make sense for
5958
// derived classes implementing `clone()`.
60-
StencilPartInterface(const StencilPartInterface &) = default;
61-
StencilPartInterface &operator=(const StencilPartInterface &) = default;
62-
};
63-
64-
/// A copyable facade for a std::unique_ptr<StencilPartInterface>. Copies result
65-
/// in a copy of the underlying pointee object.
66-
class StencilPart {
67-
public:
68-
explicit StencilPart(std::shared_ptr<StencilPartInterface> Impl)
69-
: Impl(std::move(Impl)) {}
70-
71-
/// See `StencilPartInterface::eval()`.
72-
llvm::Error eval(const ast_matchers::MatchFinder::MatchResult &Match,
73-
std::string *Result) const {
74-
return Impl->eval(Match, Result);
75-
}
76-
77-
std::string toString() const {
78-
if (Impl == nullptr)
79-
return "";
80-
return Impl->toString();
81-
}
82-
83-
private:
84-
std::shared_ptr<StencilPartInterface> Impl;
59+
StencilInterface(const StencilInterface &) = default;
60+
StencilInterface &operator=(const StencilInterface &) = default;
8561
};
8662

8763
/// A sequence of code fragments, references to parameters and code-generation
88-
/// operations that together can be evaluated to (a fragment of) source code,
89-
/// given a match result.
64+
/// operations that together can be evaluated to (a fragment of) source code or
65+
/// a diagnostic message, given a match result.
66+
///
67+
/// We use a `shared_ptr` to allow for easy and cheap copying of stencils.
68+
/// Since `StencilInterface` is an immutable interface, the sharing doesn't
69+
/// impose any risks. Otherwise, we would have to add a virtual `copy` method to
70+
/// the API and implement it for all derived classes.
9071
class Stencil {
72+
std::shared_ptr<StencilInterface> Impl;
73+
9174
public:
9275
Stencil() = default;
93-
Stencil(std::vector<StencilPart> Parts) : Parts(std::move(Parts)) {}
94-
95-
/// Composes a stencil from a series of parts.
96-
template <typename... Ts> static Stencil cat(Ts &&... Parts) {
97-
Stencil S;
98-
S.Parts = {wrap(std::forward<Ts>(Parts))...};
99-
return S;
100-
}
76+
explicit Stencil(std::shared_ptr<StencilInterface> Ptr)
77+
: Impl(std::move(Ptr)) {}
10178

102-
/// Appends data from a \p OtherStencil to this stencil.
103-
void append(Stencil OtherStencil);
79+
StencilInterface *operator->() const { return Impl.get(); }
10480

105-
// Evaluates the stencil given a match result. Requires that the nodes in the
106-
// result includes any ids referenced in the stencil. References to missing
107-
// nodes will result in an invalid_argument error.
10881
llvm::Expected<std::string>
109-
eval(const ast_matchers::MatchFinder::MatchResult &Match) const;
110-
111-
// Allow Stencils to operate as std::function, for compatibility with
112-
// Transformer's TextGenerator.
113-
llvm::Expected<std::string>
114-
operator()(const ast_matchers::MatchFinder::MatchResult &Result) const {
115-
return eval(Result);
116-
}
117-
118-
/// Constructs a string representation of the Stencil. The string is not
119-
/// guaranteed to be unique.
120-
std::string toString() const {
121-
std::vector<std::string> PartStrings;
122-
PartStrings.reserve(Parts.size());
123-
for (const auto &Part : Parts)
124-
PartStrings.push_back(Part.toString());
125-
return llvm::join(PartStrings, ", ");
82+
operator()(const ast_matchers::MatchFinder::MatchResult &R) {
83+
return Impl->eval(R);
12684
}
127-
128-
private:
129-
static StencilPart wrap(llvm::StringRef Text);
130-
static StencilPart wrap(RangeSelector Selector);
131-
static StencilPart wrap(StencilPart Part) { return Part; }
132-
133-
std::vector<StencilPart> Parts;
13485
};
13586

87+
namespace detail {
88+
/// Convenience function to construct a \c Stencil. Overloaded for common cases
89+
/// so that user doesn't need to specify which factory function to use. This
90+
/// pattern gives benefits similar to implicit constructors, while maintaing a
91+
/// higher degree of explictness.
92+
Stencil makeStencil(llvm::StringRef Text);
93+
Stencil makeStencil(RangeSelector Selector);
94+
inline Stencil makeStencil(Stencil S) { return S; }
95+
} // namespace detail
96+
97+
/// Constructs the string representing the concatenation of the given \p
98+
/// Parts. If only one element is passed in \p Parts, returns that element.
99+
Stencil catVector(std::vector<Stencil> Parts);
100+
101+
/// Concatenates 0+ stencil pieces into a single stencil. Arguments can be raw
102+
/// text, ranges in the matched code (\p RangeSelector) or other `Stencil`s.
103+
template <typename... Ts> Stencil cat(Ts &&... Parts) {
104+
return catVector({detail::makeStencil(std::forward<Ts>(Parts))...});
105+
}
106+
136107
//
137108
// Functions for conveniently building stencils.
138109
//
139110

140-
/// Convenience wrapper for Stencil::cat that can be imported with a using decl.
141-
template <typename... Ts> Stencil cat(Ts &&... Parts) {
142-
return Stencil::cat(std::forward<Ts>(Parts)...);
143-
}
144-
/// Convenience wrapper for Stencil constructor of the same type. Declaration
145-
/// outside of the class supports transition of `Stencil` type to an alias
146-
/// rather than a class.
147-
inline Stencil catVector(std::vector<StencilPart> Parts) {
148-
return Stencil(std::move(Parts));
149-
}
150-
151111
/// \returns exactly the text provided.
152-
StencilPart text(llvm::StringRef Text);
112+
Stencil text(llvm::StringRef Text);
153113

154114
/// \returns the source corresponding to the selected range.
155-
StencilPart selection(RangeSelector Selector);
115+
Stencil selection(RangeSelector Selector);
156116

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

163123
/// Constructs an idiomatic dereferencing of the expression bound to \p ExprId.
164124
/// \p ExprId is wrapped in parentheses, if needed.
165-
StencilPart deref(llvm::StringRef ExprId);
125+
Stencil deref(llvm::StringRef ExprId);
166126

167127
/// Constructs an expression that idiomatically takes the address of the
168128
/// expression bound to \p ExprId. \p ExprId is wrapped in parentheses, if
169129
/// needed.
170-
StencilPart addressOf(llvm::StringRef ExprId);
130+
Stencil addressOf(llvm::StringRef ExprId);
171131

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

182142
/// Chooses between the two stencil parts, based on whether \p ID is bound in
183143
/// the match.
184-
StencilPart ifBound(llvm::StringRef Id, StencilPart TruePart,
185-
StencilPart FalsePart);
144+
Stencil ifBound(llvm::StringRef Id, Stencil TrueStencil, Stencil FalseStencil);
186145

187146
/// Chooses between the two strings, based on whether \p ID is bound in the
188147
/// match.
189-
inline StencilPart ifBound(llvm::StringRef Id, llvm::StringRef TrueText,
190-
llvm::StringRef FalseText) {
148+
inline Stencil ifBound(llvm::StringRef Id, llvm::StringRef TrueText,
149+
llvm::StringRef FalseText) {
191150
return ifBound(Id, text(TrueText), text(FalseText));
192151
}
193152

194-
/// Wraps a MatchConsumer in a StencilPart, so that it can be used in a Stencil.
195-
/// This supports user-defined extensions to the Stencil language.
196-
StencilPart run(MatchConsumer<std::string> C);
153+
/// Wraps a \c MatchConsumer in a \c Stencil, so that it can be used in a \c
154+
/// Stencil. This supports user-defined extensions to the \c Stencil language.
155+
Stencil run(MatchConsumer<std::string> C);
197156

198157
/// For debug use only; semantics are not guaranteed.
199158
///
200159
/// \returns the string resulting from calling the node's print() method.
201-
StencilPart dPrint(llvm::StringRef Id);
160+
Stencil dPrint(llvm::StringRef Id);
202161
} // namespace transformer
203-
204-
namespace tooling {
205-
// DEPRECATED: These are temporary aliases supporting client migration to the
206-
// `transformer` namespace.
207-
using Stencil = transformer::Stencil;
208-
using StencilPart = transformer::StencilPart;
209-
namespace stencil {
210-
using transformer::access;
211-
using transformer::addressOf;
212-
using transformer::cat;
213-
using transformer::deref;
214-
using transformer::dPrint;
215-
using transformer::expression;
216-
using transformer::ifBound;
217-
using transformer::run;
218-
using transformer::selection;
219-
using transformer::text;
220-
/// \returns the source corresponding to the identified node.
221-
/// FIXME: Deprecated. Write `selection(node(Id))` instead.
222-
inline transformer::StencilPart node(llvm::StringRef Id) {
223-
return selection(tooling::node(Id));
224-
}
225-
} // namespace stencil
226-
} // namespace tooling
227162
} // namespace clang
228163
#endif // LLVM_CLANG_TOOLING_TRANSFORMER_STENCIL_H_

0 commit comments

Comments
 (0)