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