Stencil.cpp revision 357095
1//===--- Stencil.cpp - Stencil implementation -------------------*- 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#include "clang/Tooling/Transformer/Stencil.h"
10#include "clang/AST/ASTContext.h"
11#include "clang/AST/ASTTypeTraits.h"
12#include "clang/AST/Expr.h"
13#include "clang/ASTMatchers/ASTMatchFinder.h"
14#include "clang/ASTMatchers/ASTMatchers.h"
15#include "clang/Lex/Lexer.h"
16#include "clang/Tooling/Transformer/SourceCode.h"
17#include "clang/Tooling/Transformer/SourceCodeBuilders.h"
18#include "llvm/ADT/SmallVector.h"
19#include "llvm/ADT/Twine.h"
20#include "llvm/Support/Errc.h"
21#include <atomic>
22#include <memory>
23#include <string>
24
25using namespace clang;
26using namespace transformer;
27
28using ast_matchers::MatchFinder;
29using ast_type_traits::DynTypedNode;
30using llvm::errc;
31using llvm::Error;
32using llvm::Expected;
33using llvm::StringError;
34
35static llvm::Expected<DynTypedNode>
36getNode(const ast_matchers::BoundNodes &Nodes, StringRef Id) {
37  auto &NodesMap = Nodes.getMap();
38  auto It = NodesMap.find(Id);
39  if (It == NodesMap.end())
40    return llvm::make_error<llvm::StringError>(llvm::errc::invalid_argument,
41                                               "Id not bound: " + Id);
42  return It->second;
43}
44
45namespace {
46// An arbitrary fragment of code within a stencil.
47struct RawTextData {
48  explicit RawTextData(std::string T) : Text(std::move(T)) {}
49  std::string Text;
50};
51
52// A debugging operation to dump the AST for a particular (bound) AST node.
53struct DebugPrintNodeData {
54  explicit DebugPrintNodeData(std::string S) : Id(std::move(S)) {}
55  std::string Id;
56};
57
58// Operators that take a single node Id as an argument.
59enum class UnaryNodeOperator {
60  Parens,
61  Deref,
62  MaybeDeref,
63  AddressOf,
64  MaybeAddressOf,
65};
66
67// Generic container for stencil operations with a (single) node-id argument.
68struct UnaryOperationData {
69  UnaryOperationData(UnaryNodeOperator Op, std::string Id)
70      : Op(Op), Id(std::move(Id)) {}
71  UnaryNodeOperator Op;
72  std::string Id;
73};
74
75// The fragment of code corresponding to the selected range.
76struct SelectorData {
77  explicit SelectorData(RangeSelector S) : Selector(std::move(S)) {}
78  RangeSelector Selector;
79};
80
81// A stencil operation to build a member access `e.m` or `e->m`, as appropriate.
82struct AccessData {
83  AccessData(StringRef BaseId, Stencil Member)
84      : BaseId(BaseId), Member(std::move(Member)) {}
85  std::string BaseId;
86  Stencil Member;
87};
88
89struct IfBoundData {
90  IfBoundData(StringRef Id, Stencil TrueStencil, Stencil FalseStencil)
91      : Id(Id), TrueStencil(std::move(TrueStencil)),
92        FalseStencil(std::move(FalseStencil)) {}
93  std::string Id;
94  Stencil TrueStencil;
95  Stencil FalseStencil;
96};
97
98struct SequenceData {
99  SequenceData(std::vector<Stencil> Stencils) : Stencils(std::move(Stencils)) {}
100  std::vector<Stencil> Stencils;
101};
102
103std::string toStringData(const RawTextData &Data) {
104  std::string Result;
105  llvm::raw_string_ostream OS(Result);
106  OS << "\"";
107  OS.write_escaped(Data.Text);
108  OS << "\"";
109  OS.flush();
110  return Result;
111}
112
113std::string toStringData(const DebugPrintNodeData &Data) {
114  return (llvm::Twine("dPrint(\"") + Data.Id + "\")").str();
115}
116
117std::string toStringData(const UnaryOperationData &Data) {
118  StringRef OpName;
119  switch (Data.Op) {
120  case UnaryNodeOperator::Parens:
121    OpName = "expression";
122    break;
123  case UnaryNodeOperator::Deref:
124    OpName = "deref";
125    break;
126  case UnaryNodeOperator::MaybeDeref:
127    OpName = "maybeDeref";
128    break;
129  case UnaryNodeOperator::AddressOf:
130    OpName = "addressOf";
131    break;
132  case UnaryNodeOperator::MaybeAddressOf:
133    OpName = "maybeAddressOf";
134    break;
135  }
136  return (OpName + "(\"" + Data.Id + "\")").str();
137}
138
139std::string toStringData(const SelectorData &) { return "selection(...)"; }
140
141std::string toStringData(const AccessData &Data) {
142  return (llvm::Twine("access(\"") + Data.BaseId + "\", " +
143          Data.Member->toString() + ")")
144      .str();
145}
146
147std::string toStringData(const IfBoundData &Data) {
148  return (llvm::Twine("ifBound(\"") + Data.Id + "\", " +
149          Data.TrueStencil->toString() + ", " + Data.FalseStencil->toString() +
150          ")")
151      .str();
152}
153
154std::string toStringData(const MatchConsumer<std::string> &) {
155  return "run(...)";
156}
157
158std::string toStringData(const SequenceData &Data) {
159  llvm::SmallVector<std::string, 2> Parts;
160  Parts.reserve(Data.Stencils.size());
161  for (const auto &S : Data.Stencils)
162    Parts.push_back(S->toString());
163  return (llvm::Twine("seq(") + llvm::join(Parts, ", ") + ")").str();
164}
165
166// The `evalData()` overloads evaluate the given stencil data to a string, given
167// the match result, and append it to `Result`. We define an overload for each
168// type of stencil data.
169
170Error evalData(const RawTextData &Data, const MatchFinder::MatchResult &,
171               std::string *Result) {
172  Result->append(Data.Text);
173  return Error::success();
174}
175
176Error evalData(const DebugPrintNodeData &Data,
177               const MatchFinder::MatchResult &Match, std::string *Result) {
178  std::string Output;
179  llvm::raw_string_ostream Os(Output);
180  auto NodeOrErr = getNode(Match.Nodes, Data.Id);
181  if (auto Err = NodeOrErr.takeError())
182    return Err;
183  NodeOrErr->print(Os, PrintingPolicy(Match.Context->getLangOpts()));
184  *Result += Os.str();
185  return Error::success();
186}
187
188Error evalData(const UnaryOperationData &Data,
189               const MatchFinder::MatchResult &Match, std::string *Result) {
190  const auto *E = Match.Nodes.getNodeAs<Expr>(Data.Id);
191  if (E == nullptr)
192    return llvm::make_error<StringError>(
193        errc::invalid_argument, "Id not bound or not Expr: " + Data.Id);
194  llvm::Optional<std::string> Source;
195  switch (Data.Op) {
196  case UnaryNodeOperator::Parens:
197    Source = tooling::buildParens(*E, *Match.Context);
198    break;
199  case UnaryNodeOperator::Deref:
200    Source = tooling::buildDereference(*E, *Match.Context);
201    break;
202  case UnaryNodeOperator::MaybeDeref:
203    if (!E->getType()->isAnyPointerType()) {
204      *Result += tooling::getText(*E, *Match.Context);
205      return Error::success();
206    }
207    Source = tooling::buildDereference(*E, *Match.Context);
208    break;
209  case UnaryNodeOperator::AddressOf:
210    Source = tooling::buildAddressOf(*E, *Match.Context);
211    break;
212  case UnaryNodeOperator::MaybeAddressOf:
213    if (E->getType()->isAnyPointerType()) {
214      *Result += tooling::getText(*E, *Match.Context);
215      return Error::success();
216    }
217    Source = tooling::buildAddressOf(*E, *Match.Context);
218    break;
219  }
220  if (!Source)
221    return llvm::make_error<StringError>(
222        errc::invalid_argument,
223        "Could not construct expression source from ID: " + Data.Id);
224  *Result += *Source;
225  return Error::success();
226}
227
228Error evalData(const SelectorData &Data, const MatchFinder::MatchResult &Match,
229               std::string *Result) {
230  auto Range = Data.Selector(Match);
231  if (!Range)
232    return Range.takeError();
233  *Result += tooling::getText(*Range, *Match.Context);
234  return Error::success();
235}
236
237Error evalData(const AccessData &Data, const MatchFinder::MatchResult &Match,
238               std::string *Result) {
239  const auto *E = Match.Nodes.getNodeAs<Expr>(Data.BaseId);
240  if (E == nullptr)
241    return llvm::make_error<StringError>(errc::invalid_argument,
242                                         "Id not bound: " + Data.BaseId);
243  if (!E->isImplicitCXXThis()) {
244    if (llvm::Optional<std::string> S =
245            E->getType()->isAnyPointerType()
246                ? tooling::buildArrow(*E, *Match.Context)
247                : tooling::buildDot(*E, *Match.Context))
248      *Result += *S;
249    else
250      return llvm::make_error<StringError>(
251          errc::invalid_argument,
252          "Could not construct object text from ID: " + Data.BaseId);
253  }
254  return Data.Member->eval(Match, Result);
255}
256
257Error evalData(const IfBoundData &Data, const MatchFinder::MatchResult &Match,
258               std::string *Result) {
259  auto &M = Match.Nodes.getMap();
260  return (M.find(Data.Id) != M.end() ? Data.TrueStencil : Data.FalseStencil)
261      ->eval(Match, Result);
262}
263
264Error evalData(const MatchConsumer<std::string> &Fn,
265               const MatchFinder::MatchResult &Match, std::string *Result) {
266  Expected<std::string> Value = Fn(Match);
267  if (!Value)
268    return Value.takeError();
269  *Result += *Value;
270  return Error::success();
271}
272
273Error evalData(const SequenceData &Data, const MatchFinder::MatchResult &Match,
274               std::string *Result) {
275  for (const auto &S : Data.Stencils)
276    if (auto Err = S->eval(Match, Result))
277      return Err;
278  return Error::success();
279}
280
281template <typename T> class StencilImpl : public StencilInterface {
282  T Data;
283
284public:
285  template <typename... Ps>
286  explicit StencilImpl(Ps &&... Args) : Data(std::forward<Ps>(Args)...) {}
287
288  Error eval(const MatchFinder::MatchResult &Match,
289             std::string *Result) const override {
290    return evalData(Data, Match, Result);
291  }
292
293  std::string toString() const override { return toStringData(Data); }
294};
295} // namespace
296
297Stencil transformer::detail::makeStencil(StringRef Text) { return text(Text); }
298
299Stencil transformer::detail::makeStencil(RangeSelector Selector) {
300  return selection(std::move(Selector));
301}
302
303Stencil transformer::text(StringRef Text) {
304  return std::make_shared<StencilImpl<RawTextData>>(Text);
305}
306
307Stencil transformer::selection(RangeSelector Selector) {
308  return std::make_shared<StencilImpl<SelectorData>>(std::move(Selector));
309}
310
311Stencil transformer::dPrint(StringRef Id) {
312  return std::make_shared<StencilImpl<DebugPrintNodeData>>(Id);
313}
314
315Stencil transformer::expression(llvm::StringRef Id) {
316  return std::make_shared<StencilImpl<UnaryOperationData>>(
317      UnaryNodeOperator::Parens, Id);
318}
319
320Stencil transformer::deref(llvm::StringRef ExprId) {
321  return std::make_shared<StencilImpl<UnaryOperationData>>(
322      UnaryNodeOperator::Deref, ExprId);
323}
324
325Stencil transformer::maybeDeref(llvm::StringRef ExprId) {
326  return std::make_shared<StencilImpl<UnaryOperationData>>(
327      UnaryNodeOperator::MaybeDeref, ExprId);
328}
329
330Stencil transformer::addressOf(llvm::StringRef ExprId) {
331  return std::make_shared<StencilImpl<UnaryOperationData>>(
332      UnaryNodeOperator::AddressOf, ExprId);
333}
334
335Stencil transformer::maybeAddressOf(llvm::StringRef ExprId) {
336  return std::make_shared<StencilImpl<UnaryOperationData>>(
337      UnaryNodeOperator::MaybeAddressOf, ExprId);
338}
339
340Stencil transformer::access(StringRef BaseId, Stencil Member) {
341  return std::make_shared<StencilImpl<AccessData>>(BaseId, std::move(Member));
342}
343
344Stencil transformer::ifBound(StringRef Id, Stencil TrueStencil,
345                             Stencil FalseStencil) {
346  return std::make_shared<StencilImpl<IfBoundData>>(Id, std::move(TrueStencil),
347                                                    std::move(FalseStencil));
348}
349
350Stencil transformer::run(MatchConsumer<std::string> Fn) {
351  return std::make_shared<StencilImpl<MatchConsumer<std::string>>>(
352      std::move(Fn));
353}
354
355Stencil transformer::catVector(std::vector<Stencil> Parts) {
356  // Only one argument, so don't wrap in sequence.
357  if (Parts.size() == 1)
358    return std::move(Parts[0]);
359  return std::make_shared<StencilImpl<SequenceData>>(std::move(Parts));
360}
361