1353942Sdim//===--- Stencil.cpp - Stencil implementation -------------------*- C++ -*-===//
2353942Sdim//
3353942Sdim// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4353942Sdim// See https://llvm.org/LICENSE.txt for license information.
5353942Sdim// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6353942Sdim//
7353942Sdim//===----------------------------------------------------------------------===//
8353942Sdim
9353942Sdim#include "clang/Tooling/Transformer/Stencil.h"
10353942Sdim#include "clang/AST/ASTContext.h"
11353942Sdim#include "clang/AST/ASTTypeTraits.h"
12353942Sdim#include "clang/AST/Expr.h"
13353942Sdim#include "clang/ASTMatchers/ASTMatchFinder.h"
14353942Sdim#include "clang/ASTMatchers/ASTMatchers.h"
15353942Sdim#include "clang/Lex/Lexer.h"
16353942Sdim#include "clang/Tooling/Transformer/SourceCode.h"
17353942Sdim#include "clang/Tooling/Transformer/SourceCodeBuilders.h"
18357095Sdim#include "llvm/ADT/SmallVector.h"
19353942Sdim#include "llvm/ADT/Twine.h"
20353942Sdim#include "llvm/Support/Errc.h"
21353942Sdim#include <atomic>
22353942Sdim#include <memory>
23353942Sdim#include <string>
24353942Sdim
25353942Sdimusing namespace clang;
26353942Sdimusing namespace transformer;
27353942Sdim
28353942Sdimusing ast_matchers::MatchFinder;
29353942Sdimusing ast_type_traits::DynTypedNode;
30353942Sdimusing llvm::errc;
31353942Sdimusing llvm::Error;
32353942Sdimusing llvm::Expected;
33353942Sdimusing llvm::StringError;
34353942Sdim
35353942Sdimstatic llvm::Expected<DynTypedNode>
36353942SdimgetNode(const ast_matchers::BoundNodes &Nodes, StringRef Id) {
37353942Sdim  auto &NodesMap = Nodes.getMap();
38353942Sdim  auto It = NodesMap.find(Id);
39353942Sdim  if (It == NodesMap.end())
40353942Sdim    return llvm::make_error<llvm::StringError>(llvm::errc::invalid_argument,
41353942Sdim                                               "Id not bound: " + Id);
42353942Sdim  return It->second;
43353942Sdim}
44353942Sdim
45353942Sdimnamespace {
46353942Sdim// An arbitrary fragment of code within a stencil.
47353942Sdimstruct RawTextData {
48353942Sdim  explicit RawTextData(std::string T) : Text(std::move(T)) {}
49353942Sdim  std::string Text;
50353942Sdim};
51353942Sdim
52353942Sdim// A debugging operation to dump the AST for a particular (bound) AST node.
53353942Sdimstruct DebugPrintNodeData {
54353942Sdim  explicit DebugPrintNodeData(std::string S) : Id(std::move(S)) {}
55353942Sdim  std::string Id;
56353942Sdim};
57353942Sdim
58353942Sdim// Operators that take a single node Id as an argument.
59353942Sdimenum class UnaryNodeOperator {
60353942Sdim  Parens,
61353942Sdim  Deref,
62357095Sdim  MaybeDeref,
63357095Sdim  AddressOf,
64357095Sdim  MaybeAddressOf,
65353942Sdim};
66353942Sdim
67353942Sdim// Generic container for stencil operations with a (single) node-id argument.
68353942Sdimstruct UnaryOperationData {
69353942Sdim  UnaryOperationData(UnaryNodeOperator Op, std::string Id)
70353942Sdim      : Op(Op), Id(std::move(Id)) {}
71353942Sdim  UnaryNodeOperator Op;
72353942Sdim  std::string Id;
73353942Sdim};
74353942Sdim
75353942Sdim// The fragment of code corresponding to the selected range.
76353942Sdimstruct SelectorData {
77353942Sdim  explicit SelectorData(RangeSelector S) : Selector(std::move(S)) {}
78353942Sdim  RangeSelector Selector;
79353942Sdim};
80353942Sdim
81353942Sdim// A stencil operation to build a member access `e.m` or `e->m`, as appropriate.
82353942Sdimstruct AccessData {
83357095Sdim  AccessData(StringRef BaseId, Stencil Member)
84353942Sdim      : BaseId(BaseId), Member(std::move(Member)) {}
85353942Sdim  std::string BaseId;
86357095Sdim  Stencil Member;
87353942Sdim};
88353942Sdim
89353942Sdimstruct IfBoundData {
90357095Sdim  IfBoundData(StringRef Id, Stencil TrueStencil, Stencil FalseStencil)
91357095Sdim      : Id(Id), TrueStencil(std::move(TrueStencil)),
92357095Sdim        FalseStencil(std::move(FalseStencil)) {}
93353942Sdim  std::string Id;
94357095Sdim  Stencil TrueStencil;
95357095Sdim  Stencil FalseStencil;
96353942Sdim};
97353942Sdim
98357095Sdimstruct SequenceData {
99357095Sdim  SequenceData(std::vector<Stencil> Stencils) : Stencils(std::move(Stencils)) {}
100357095Sdim  std::vector<Stencil> Stencils;
101357095Sdim};
102357095Sdim
103353942Sdimstd::string toStringData(const RawTextData &Data) {
104353942Sdim  std::string Result;
105353942Sdim  llvm::raw_string_ostream OS(Result);
106353942Sdim  OS << "\"";
107353942Sdim  OS.write_escaped(Data.Text);
108353942Sdim  OS << "\"";
109353942Sdim  OS.flush();
110353942Sdim  return Result;
111353942Sdim}
112353942Sdim
113353942Sdimstd::string toStringData(const DebugPrintNodeData &Data) {
114353942Sdim  return (llvm::Twine("dPrint(\"") + Data.Id + "\")").str();
115353942Sdim}
116353942Sdim
117353942Sdimstd::string toStringData(const UnaryOperationData &Data) {
118353942Sdim  StringRef OpName;
119353942Sdim  switch (Data.Op) {
120353942Sdim  case UnaryNodeOperator::Parens:
121353942Sdim    OpName = "expression";
122353942Sdim    break;
123353942Sdim  case UnaryNodeOperator::Deref:
124353942Sdim    OpName = "deref";
125353942Sdim    break;
126357095Sdim  case UnaryNodeOperator::MaybeDeref:
127357095Sdim    OpName = "maybeDeref";
128357095Sdim    break;
129357095Sdim  case UnaryNodeOperator::AddressOf:
130353942Sdim    OpName = "addressOf";
131353942Sdim    break;
132357095Sdim  case UnaryNodeOperator::MaybeAddressOf:
133357095Sdim    OpName = "maybeAddressOf";
134357095Sdim    break;
135353942Sdim  }
136353942Sdim  return (OpName + "(\"" + Data.Id + "\")").str();
137353942Sdim}
138353942Sdim
139353942Sdimstd::string toStringData(const SelectorData &) { return "selection(...)"; }
140353942Sdim
141353942Sdimstd::string toStringData(const AccessData &Data) {
142353942Sdim  return (llvm::Twine("access(\"") + Data.BaseId + "\", " +
143357095Sdim          Data.Member->toString() + ")")
144353942Sdim      .str();
145353942Sdim}
146353942Sdim
147353942Sdimstd::string toStringData(const IfBoundData &Data) {
148353942Sdim  return (llvm::Twine("ifBound(\"") + Data.Id + "\", " +
149357095Sdim          Data.TrueStencil->toString() + ", " + Data.FalseStencil->toString() +
150357095Sdim          ")")
151353942Sdim      .str();
152353942Sdim}
153353942Sdim
154353942Sdimstd::string toStringData(const MatchConsumer<std::string> &) {
155353942Sdim  return "run(...)";
156353942Sdim}
157353942Sdim
158357095Sdimstd::string toStringData(const SequenceData &Data) {
159357095Sdim  llvm::SmallVector<std::string, 2> Parts;
160357095Sdim  Parts.reserve(Data.Stencils.size());
161357095Sdim  for (const auto &S : Data.Stencils)
162357095Sdim    Parts.push_back(S->toString());
163357095Sdim  return (llvm::Twine("seq(") + llvm::join(Parts, ", ") + ")").str();
164357095Sdim}
165357095Sdim
166353942Sdim// The `evalData()` overloads evaluate the given stencil data to a string, given
167353942Sdim// the match result, and append it to `Result`. We define an overload for each
168353942Sdim// type of stencil data.
169353942Sdim
170353942SdimError evalData(const RawTextData &Data, const MatchFinder::MatchResult &,
171353942Sdim               std::string *Result) {
172353942Sdim  Result->append(Data.Text);
173353942Sdim  return Error::success();
174353942Sdim}
175353942Sdim
176353942SdimError evalData(const DebugPrintNodeData &Data,
177353942Sdim               const MatchFinder::MatchResult &Match, std::string *Result) {
178353942Sdim  std::string Output;
179353942Sdim  llvm::raw_string_ostream Os(Output);
180353942Sdim  auto NodeOrErr = getNode(Match.Nodes, Data.Id);
181353942Sdim  if (auto Err = NodeOrErr.takeError())
182353942Sdim    return Err;
183353942Sdim  NodeOrErr->print(Os, PrintingPolicy(Match.Context->getLangOpts()));
184353942Sdim  *Result += Os.str();
185353942Sdim  return Error::success();
186353942Sdim}
187353942Sdim
188353942SdimError evalData(const UnaryOperationData &Data,
189353942Sdim               const MatchFinder::MatchResult &Match, std::string *Result) {
190353942Sdim  const auto *E = Match.Nodes.getNodeAs<Expr>(Data.Id);
191353942Sdim  if (E == nullptr)
192353942Sdim    return llvm::make_error<StringError>(
193353942Sdim        errc::invalid_argument, "Id not bound or not Expr: " + Data.Id);
194353942Sdim  llvm::Optional<std::string> Source;
195353942Sdim  switch (Data.Op) {
196353942Sdim  case UnaryNodeOperator::Parens:
197353942Sdim    Source = tooling::buildParens(*E, *Match.Context);
198353942Sdim    break;
199353942Sdim  case UnaryNodeOperator::Deref:
200353942Sdim    Source = tooling::buildDereference(*E, *Match.Context);
201353942Sdim    break;
202357095Sdim  case UnaryNodeOperator::MaybeDeref:
203357095Sdim    if (!E->getType()->isAnyPointerType()) {
204357095Sdim      *Result += tooling::getText(*E, *Match.Context);
205357095Sdim      return Error::success();
206357095Sdim    }
207357095Sdim    Source = tooling::buildDereference(*E, *Match.Context);
208357095Sdim    break;
209357095Sdim  case UnaryNodeOperator::AddressOf:
210353942Sdim    Source = tooling::buildAddressOf(*E, *Match.Context);
211353942Sdim    break;
212357095Sdim  case UnaryNodeOperator::MaybeAddressOf:
213357095Sdim    if (E->getType()->isAnyPointerType()) {
214357095Sdim      *Result += tooling::getText(*E, *Match.Context);
215357095Sdim      return Error::success();
216357095Sdim    }
217357095Sdim    Source = tooling::buildAddressOf(*E, *Match.Context);
218357095Sdim    break;
219353942Sdim  }
220353942Sdim  if (!Source)
221353942Sdim    return llvm::make_error<StringError>(
222353942Sdim        errc::invalid_argument,
223353942Sdim        "Could not construct expression source from ID: " + Data.Id);
224353942Sdim  *Result += *Source;
225353942Sdim  return Error::success();
226353942Sdim}
227353942Sdim
228353942SdimError evalData(const SelectorData &Data, const MatchFinder::MatchResult &Match,
229353942Sdim               std::string *Result) {
230353942Sdim  auto Range = Data.Selector(Match);
231353942Sdim  if (!Range)
232353942Sdim    return Range.takeError();
233353942Sdim  *Result += tooling::getText(*Range, *Match.Context);
234353942Sdim  return Error::success();
235353942Sdim}
236353942Sdim
237353942SdimError evalData(const AccessData &Data, const MatchFinder::MatchResult &Match,
238353942Sdim               std::string *Result) {
239353942Sdim  const auto *E = Match.Nodes.getNodeAs<Expr>(Data.BaseId);
240353942Sdim  if (E == nullptr)
241353942Sdim    return llvm::make_error<StringError>(errc::invalid_argument,
242353942Sdim                                         "Id not bound: " + Data.BaseId);
243353942Sdim  if (!E->isImplicitCXXThis()) {
244353942Sdim    if (llvm::Optional<std::string> S =
245353942Sdim            E->getType()->isAnyPointerType()
246353942Sdim                ? tooling::buildArrow(*E, *Match.Context)
247353942Sdim                : tooling::buildDot(*E, *Match.Context))
248353942Sdim      *Result += *S;
249353942Sdim    else
250353942Sdim      return llvm::make_error<StringError>(
251353942Sdim          errc::invalid_argument,
252353942Sdim          "Could not construct object text from ID: " + Data.BaseId);
253353942Sdim  }
254357095Sdim  return Data.Member->eval(Match, Result);
255353942Sdim}
256353942Sdim
257353942SdimError evalData(const IfBoundData &Data, const MatchFinder::MatchResult &Match,
258353942Sdim               std::string *Result) {
259353942Sdim  auto &M = Match.Nodes.getMap();
260357095Sdim  return (M.find(Data.Id) != M.end() ? Data.TrueStencil : Data.FalseStencil)
261357095Sdim      ->eval(Match, Result);
262353942Sdim}
263353942Sdim
264353942SdimError evalData(const MatchConsumer<std::string> &Fn,
265353942Sdim               const MatchFinder::MatchResult &Match, std::string *Result) {
266353942Sdim  Expected<std::string> Value = Fn(Match);
267353942Sdim  if (!Value)
268353942Sdim    return Value.takeError();
269353942Sdim  *Result += *Value;
270353942Sdim  return Error::success();
271353942Sdim}
272353942Sdim
273357095SdimError evalData(const SequenceData &Data, const MatchFinder::MatchResult &Match,
274357095Sdim               std::string *Result) {
275357095Sdim  for (const auto &S : Data.Stencils)
276357095Sdim    if (auto Err = S->eval(Match, Result))
277357095Sdim      return Err;
278357095Sdim  return Error::success();
279357095Sdim}
280357095Sdim
281357095Sdimtemplate <typename T> class StencilImpl : public StencilInterface {
282353942Sdim  T Data;
283353942Sdim
284353942Sdimpublic:
285353942Sdim  template <typename... Ps>
286357095Sdim  explicit StencilImpl(Ps &&... Args) : Data(std::forward<Ps>(Args)...) {}
287353942Sdim
288353942Sdim  Error eval(const MatchFinder::MatchResult &Match,
289353942Sdim             std::string *Result) const override {
290353942Sdim    return evalData(Data, Match, Result);
291353942Sdim  }
292353942Sdim
293353942Sdim  std::string toString() const override { return toStringData(Data); }
294353942Sdim};
295353942Sdim} // namespace
296353942Sdim
297357095SdimStencil transformer::detail::makeStencil(StringRef Text) { return text(Text); }
298357095Sdim
299357095SdimStencil transformer::detail::makeStencil(RangeSelector Selector) {
300357095Sdim  return selection(std::move(Selector));
301353942Sdim}
302353942Sdim
303357095SdimStencil transformer::text(StringRef Text) {
304357095Sdim  return std::make_shared<StencilImpl<RawTextData>>(Text);
305353942Sdim}
306353942Sdim
307357095SdimStencil transformer::selection(RangeSelector Selector) {
308357095Sdim  return std::make_shared<StencilImpl<SelectorData>>(std::move(Selector));
309353942Sdim}
310353942Sdim
311357095SdimStencil transformer::dPrint(StringRef Id) {
312357095Sdim  return std::make_shared<StencilImpl<DebugPrintNodeData>>(Id);
313353942Sdim}
314353942Sdim
315357095SdimStencil transformer::expression(llvm::StringRef Id) {
316357095Sdim  return std::make_shared<StencilImpl<UnaryOperationData>>(
317357095Sdim      UnaryNodeOperator::Parens, Id);
318353942Sdim}
319353942Sdim
320357095SdimStencil transformer::deref(llvm::StringRef ExprId) {
321357095Sdim  return std::make_shared<StencilImpl<UnaryOperationData>>(
322357095Sdim      UnaryNodeOperator::Deref, ExprId);
323353942Sdim}
324353942Sdim
325357095SdimStencil transformer::maybeDeref(llvm::StringRef ExprId) {
326357095Sdim  return std::make_shared<StencilImpl<UnaryOperationData>>(
327357095Sdim      UnaryNodeOperator::MaybeDeref, ExprId);
328353942Sdim}
329353942Sdim
330357095SdimStencil transformer::addressOf(llvm::StringRef ExprId) {
331357095Sdim  return std::make_shared<StencilImpl<UnaryOperationData>>(
332357095Sdim      UnaryNodeOperator::AddressOf, ExprId);
333353942Sdim}
334353942Sdim
335357095SdimStencil transformer::maybeAddressOf(llvm::StringRef ExprId) {
336357095Sdim  return std::make_shared<StencilImpl<UnaryOperationData>>(
337357095Sdim      UnaryNodeOperator::MaybeAddressOf, ExprId);
338353942Sdim}
339353942Sdim
340357095SdimStencil transformer::access(StringRef BaseId, Stencil Member) {
341357095Sdim  return std::make_shared<StencilImpl<AccessData>>(BaseId, std::move(Member));
342353942Sdim}
343353942Sdim
344357095SdimStencil transformer::ifBound(StringRef Id, Stencil TrueStencil,
345357095Sdim                             Stencil FalseStencil) {
346357095Sdim  return std::make_shared<StencilImpl<IfBoundData>>(Id, std::move(TrueStencil),
347357095Sdim                                                    std::move(FalseStencil));
348353942Sdim}
349353942Sdim
350357095SdimStencil transformer::run(MatchConsumer<std::string> Fn) {
351357095Sdim  return std::make_shared<StencilImpl<MatchConsumer<std::string>>>(
352357095Sdim      std::move(Fn));
353353942Sdim}
354353942Sdim
355357095SdimStencil transformer::catVector(std::vector<Stencil> Parts) {
356357095Sdim  // Only one argument, so don't wrap in sequence.
357357095Sdim  if (Parts.size() == 1)
358357095Sdim    return std::move(Parts[0]);
359357095Sdim  return std::make_shared<StencilImpl<SequenceData>>(std::move(Parts));
360353942Sdim}
361