Stencil.cpp revision 353942
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"
18353942Sdim#include "llvm/ADT/Twine.h"
19353942Sdim#include "llvm/Support/Errc.h"
20353942Sdim#include <atomic>
21353942Sdim#include <memory>
22353942Sdim#include <string>
23353942Sdim
24353942Sdimusing namespace clang;
25353942Sdimusing namespace transformer;
26353942Sdim
27353942Sdimusing ast_matchers::MatchFinder;
28353942Sdimusing ast_type_traits::DynTypedNode;
29353942Sdimusing llvm::errc;
30353942Sdimusing llvm::Error;
31353942Sdimusing llvm::Expected;
32353942Sdimusing llvm::StringError;
33353942Sdim
34353942Sdimstatic llvm::Expected<DynTypedNode>
35353942SdimgetNode(const ast_matchers::BoundNodes &Nodes, StringRef Id) {
36353942Sdim  auto &NodesMap = Nodes.getMap();
37353942Sdim  auto It = NodesMap.find(Id);
38353942Sdim  if (It == NodesMap.end())
39353942Sdim    return llvm::make_error<llvm::StringError>(llvm::errc::invalid_argument,
40353942Sdim                                               "Id not bound: " + Id);
41353942Sdim  return It->second;
42353942Sdim}
43353942Sdim
44353942Sdimnamespace {
45353942Sdim// An arbitrary fragment of code within a stencil.
46353942Sdimstruct RawTextData {
47353942Sdim  explicit RawTextData(std::string T) : Text(std::move(T)) {}
48353942Sdim  std::string Text;
49353942Sdim};
50353942Sdim
51353942Sdim// A debugging operation to dump the AST for a particular (bound) AST node.
52353942Sdimstruct DebugPrintNodeData {
53353942Sdim  explicit DebugPrintNodeData(std::string S) : Id(std::move(S)) {}
54353942Sdim  std::string Id;
55353942Sdim};
56353942Sdim
57353942Sdim// Operators that take a single node Id as an argument.
58353942Sdimenum class UnaryNodeOperator {
59353942Sdim  Parens,
60353942Sdim  Deref,
61353942Sdim  Address,
62353942Sdim};
63353942Sdim
64353942Sdim// Generic container for stencil operations with a (single) node-id argument.
65353942Sdimstruct UnaryOperationData {
66353942Sdim  UnaryOperationData(UnaryNodeOperator Op, std::string Id)
67353942Sdim      : Op(Op), Id(std::move(Id)) {}
68353942Sdim  UnaryNodeOperator Op;
69353942Sdim  std::string Id;
70353942Sdim};
71353942Sdim
72353942Sdim// The fragment of code corresponding to the selected range.
73353942Sdimstruct SelectorData {
74353942Sdim  explicit SelectorData(RangeSelector S) : Selector(std::move(S)) {}
75353942Sdim  RangeSelector Selector;
76353942Sdim};
77353942Sdim
78353942Sdim// A stencil operation to build a member access `e.m` or `e->m`, as appropriate.
79353942Sdimstruct AccessData {
80353942Sdim  AccessData(StringRef BaseId, StencilPart Member)
81353942Sdim      : BaseId(BaseId), Member(std::move(Member)) {}
82353942Sdim  std::string BaseId;
83353942Sdim  StencilPart Member;
84353942Sdim};
85353942Sdim
86353942Sdimstruct IfBoundData {
87353942Sdim  IfBoundData(StringRef Id, StencilPart TruePart, StencilPart FalsePart)
88353942Sdim      : Id(Id), TruePart(std::move(TruePart)), FalsePart(std::move(FalsePart)) {
89353942Sdim  }
90353942Sdim  std::string Id;
91353942Sdim  StencilPart TruePart;
92353942Sdim  StencilPart FalsePart;
93353942Sdim};
94353942Sdim
95353942Sdimstd::string toStringData(const RawTextData &Data) {
96353942Sdim  std::string Result;
97353942Sdim  llvm::raw_string_ostream OS(Result);
98353942Sdim  OS << "\"";
99353942Sdim  OS.write_escaped(Data.Text);
100353942Sdim  OS << "\"";
101353942Sdim  OS.flush();
102353942Sdim  return Result;
103353942Sdim}
104353942Sdim
105353942Sdimstd::string toStringData(const DebugPrintNodeData &Data) {
106353942Sdim  return (llvm::Twine("dPrint(\"") + Data.Id + "\")").str();
107353942Sdim}
108353942Sdim
109353942Sdimstd::string toStringData(const UnaryOperationData &Data) {
110353942Sdim  StringRef OpName;
111353942Sdim  switch (Data.Op) {
112353942Sdim  case UnaryNodeOperator::Parens:
113353942Sdim    OpName = "expression";
114353942Sdim    break;
115353942Sdim  case UnaryNodeOperator::Deref:
116353942Sdim    OpName = "deref";
117353942Sdim    break;
118353942Sdim  case UnaryNodeOperator::Address:
119353942Sdim    OpName = "addressOf";
120353942Sdim    break;
121353942Sdim  }
122353942Sdim  return (OpName + "(\"" + Data.Id + "\")").str();
123353942Sdim}
124353942Sdim
125353942Sdimstd::string toStringData(const SelectorData &) { return "selection(...)"; }
126353942Sdim
127353942Sdimstd::string toStringData(const AccessData &Data) {
128353942Sdim  return (llvm::Twine("access(\"") + Data.BaseId + "\", " +
129353942Sdim          Data.Member.toString() + ")")
130353942Sdim      .str();
131353942Sdim}
132353942Sdim
133353942Sdimstd::string toStringData(const IfBoundData &Data) {
134353942Sdim  return (llvm::Twine("ifBound(\"") + Data.Id + "\", " +
135353942Sdim          Data.TruePart.toString() + ", " + Data.FalsePart.toString() + ")")
136353942Sdim      .str();
137353942Sdim}
138353942Sdim
139353942Sdimstd::string toStringData(const MatchConsumer<std::string> &) {
140353942Sdim  return "run(...)";
141353942Sdim}
142353942Sdim
143353942Sdim// The `evalData()` overloads evaluate the given stencil data to a string, given
144353942Sdim// the match result, and append it to `Result`. We define an overload for each
145353942Sdim// type of stencil data.
146353942Sdim
147353942SdimError evalData(const RawTextData &Data, const MatchFinder::MatchResult &,
148353942Sdim               std::string *Result) {
149353942Sdim  Result->append(Data.Text);
150353942Sdim  return Error::success();
151353942Sdim}
152353942Sdim
153353942SdimError evalData(const DebugPrintNodeData &Data,
154353942Sdim               const MatchFinder::MatchResult &Match, std::string *Result) {
155353942Sdim  std::string Output;
156353942Sdim  llvm::raw_string_ostream Os(Output);
157353942Sdim  auto NodeOrErr = getNode(Match.Nodes, Data.Id);
158353942Sdim  if (auto Err = NodeOrErr.takeError())
159353942Sdim    return Err;
160353942Sdim  NodeOrErr->print(Os, PrintingPolicy(Match.Context->getLangOpts()));
161353942Sdim  *Result += Os.str();
162353942Sdim  return Error::success();
163353942Sdim}
164353942Sdim
165353942SdimError evalData(const UnaryOperationData &Data,
166353942Sdim               const MatchFinder::MatchResult &Match, std::string *Result) {
167353942Sdim  const auto *E = Match.Nodes.getNodeAs<Expr>(Data.Id);
168353942Sdim  if (E == nullptr)
169353942Sdim    return llvm::make_error<StringError>(
170353942Sdim        errc::invalid_argument, "Id not bound or not Expr: " + Data.Id);
171353942Sdim  llvm::Optional<std::string> Source;
172353942Sdim  switch (Data.Op) {
173353942Sdim  case UnaryNodeOperator::Parens:
174353942Sdim    Source = tooling::buildParens(*E, *Match.Context);
175353942Sdim    break;
176353942Sdim  case UnaryNodeOperator::Deref:
177353942Sdim    Source = tooling::buildDereference(*E, *Match.Context);
178353942Sdim    break;
179353942Sdim  case UnaryNodeOperator::Address:
180353942Sdim    Source = tooling::buildAddressOf(*E, *Match.Context);
181353942Sdim    break;
182353942Sdim  }
183353942Sdim  if (!Source)
184353942Sdim    return llvm::make_error<StringError>(
185353942Sdim        errc::invalid_argument,
186353942Sdim        "Could not construct expression source from ID: " + Data.Id);
187353942Sdim  *Result += *Source;
188353942Sdim  return Error::success();
189353942Sdim}
190353942Sdim
191353942SdimError evalData(const SelectorData &Data, const MatchFinder::MatchResult &Match,
192353942Sdim               std::string *Result) {
193353942Sdim  auto Range = Data.Selector(Match);
194353942Sdim  if (!Range)
195353942Sdim    return Range.takeError();
196353942Sdim  *Result += tooling::getText(*Range, *Match.Context);
197353942Sdim  return Error::success();
198353942Sdim}
199353942Sdim
200353942SdimError evalData(const AccessData &Data, const MatchFinder::MatchResult &Match,
201353942Sdim               std::string *Result) {
202353942Sdim  const auto *E = Match.Nodes.getNodeAs<Expr>(Data.BaseId);
203353942Sdim  if (E == nullptr)
204353942Sdim    return llvm::make_error<StringError>(errc::invalid_argument,
205353942Sdim                                         "Id not bound: " + Data.BaseId);
206353942Sdim  if (!E->isImplicitCXXThis()) {
207353942Sdim    if (llvm::Optional<std::string> S =
208353942Sdim            E->getType()->isAnyPointerType()
209353942Sdim                ? tooling::buildArrow(*E, *Match.Context)
210353942Sdim                : tooling::buildDot(*E, *Match.Context))
211353942Sdim      *Result += *S;
212353942Sdim    else
213353942Sdim      return llvm::make_error<StringError>(
214353942Sdim          errc::invalid_argument,
215353942Sdim          "Could not construct object text from ID: " + Data.BaseId);
216353942Sdim  }
217353942Sdim  return Data.Member.eval(Match, Result);
218353942Sdim}
219353942Sdim
220353942SdimError evalData(const IfBoundData &Data, const MatchFinder::MatchResult &Match,
221353942Sdim               std::string *Result) {
222353942Sdim  auto &M = Match.Nodes.getMap();
223353942Sdim  return (M.find(Data.Id) != M.end() ? Data.TruePart : Data.FalsePart)
224353942Sdim      .eval(Match, Result);
225353942Sdim}
226353942Sdim
227353942SdimError evalData(const MatchConsumer<std::string> &Fn,
228353942Sdim               const MatchFinder::MatchResult &Match, std::string *Result) {
229353942Sdim  Expected<std::string> Value = Fn(Match);
230353942Sdim  if (!Value)
231353942Sdim    return Value.takeError();
232353942Sdim  *Result += *Value;
233353942Sdim  return Error::success();
234353942Sdim}
235353942Sdim
236353942Sdimtemplate <typename T>
237353942Sdimclass StencilPartImpl : public StencilPartInterface {
238353942Sdim  T Data;
239353942Sdim
240353942Sdimpublic:
241353942Sdim  template <typename... Ps>
242353942Sdim  explicit StencilPartImpl(Ps &&... Args) : Data(std::forward<Ps>(Args)...) {}
243353942Sdim
244353942Sdim  Error eval(const MatchFinder::MatchResult &Match,
245353942Sdim             std::string *Result) const override {
246353942Sdim    return evalData(Data, Match, Result);
247353942Sdim  }
248353942Sdim
249353942Sdim  std::string toString() const override { return toStringData(Data); }
250353942Sdim};
251353942Sdim} // namespace
252353942Sdim
253353942SdimStencilPart Stencil::wrap(StringRef Text) {
254353942Sdim  return transformer::text(Text);
255353942Sdim}
256353942Sdim
257353942SdimStencilPart Stencil::wrap(RangeSelector Selector) {
258353942Sdim  return transformer::selection(std::move(Selector));
259353942Sdim}
260353942Sdim
261353942Sdimvoid Stencil::append(Stencil OtherStencil) {
262353942Sdim  for (auto &Part : OtherStencil.Parts)
263353942Sdim    Parts.push_back(std::move(Part));
264353942Sdim}
265353942Sdim
266353942Sdimllvm::Expected<std::string>
267353942SdimStencil::eval(const MatchFinder::MatchResult &Match) const {
268353942Sdim  std::string Result;
269353942Sdim  for (const auto &Part : Parts)
270353942Sdim    if (auto Err = Part.eval(Match, &Result))
271353942Sdim      return std::move(Err);
272353942Sdim  return Result;
273353942Sdim}
274353942Sdim
275353942SdimStencilPart transformer::text(StringRef Text) {
276353942Sdim  return StencilPart(std::make_shared<StencilPartImpl<RawTextData>>(Text));
277353942Sdim}
278353942Sdim
279353942SdimStencilPart transformer::selection(RangeSelector Selector) {
280353942Sdim  return StencilPart(
281353942Sdim      std::make_shared<StencilPartImpl<SelectorData>>(std::move(Selector)));
282353942Sdim}
283353942Sdim
284353942SdimStencilPart transformer::dPrint(StringRef Id) {
285353942Sdim  return StencilPart(std::make_shared<StencilPartImpl<DebugPrintNodeData>>(Id));
286353942Sdim}
287353942Sdim
288353942SdimStencilPart transformer::expression(llvm::StringRef Id) {
289353942Sdim  return StencilPart(std::make_shared<StencilPartImpl<UnaryOperationData>>(
290353942Sdim      UnaryNodeOperator::Parens, Id));
291353942Sdim}
292353942Sdim
293353942SdimStencilPart transformer::deref(llvm::StringRef ExprId) {
294353942Sdim  return StencilPart(std::make_shared<StencilPartImpl<UnaryOperationData>>(
295353942Sdim      UnaryNodeOperator::Deref, ExprId));
296353942Sdim}
297353942Sdim
298353942SdimStencilPart transformer::addressOf(llvm::StringRef ExprId) {
299353942Sdim  return StencilPart(std::make_shared<StencilPartImpl<UnaryOperationData>>(
300353942Sdim      UnaryNodeOperator::Address, ExprId));
301353942Sdim}
302353942Sdim
303353942SdimStencilPart transformer::access(StringRef BaseId, StencilPart Member) {
304353942Sdim  return StencilPart(
305353942Sdim      std::make_shared<StencilPartImpl<AccessData>>(BaseId, std::move(Member)));
306353942Sdim}
307353942Sdim
308353942SdimStencilPart transformer::ifBound(StringRef Id, StencilPart TruePart,
309353942Sdim                             StencilPart FalsePart) {
310353942Sdim  return StencilPart(std::make_shared<StencilPartImpl<IfBoundData>>(
311353942Sdim      Id, std::move(TruePart), std::move(FalsePart)));
312353942Sdim}
313353942Sdim
314353942SdimStencilPart transformer::run(MatchConsumer<std::string> Fn) {
315353942Sdim  return StencilPart(
316353942Sdim      std::make_shared<StencilPartImpl<MatchConsumer<std::string>>>(
317353942Sdim          std::move(Fn)));
318353942Sdim}
319