xref: /freebsd/contrib/llvm-project/clang/lib/Tooling/Transformer/Stencil.cpp (revision c66ec88fed842fbaad62c30d510644ceb7bd2d71)
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 
27 using namespace clang;
28 using namespace transformer;
29 
30 using ast_matchers::MatchFinder;
31 using llvm::errc;
32 using llvm::Error;
33 using llvm::Expected;
34 using llvm::StringError;
35 
36 static llvm::Expected<DynTypedNode>
37 getNode(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 
46 namespace {
47 // An arbitrary fragment of code within a stencil.
48 struct 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.
54 struct 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.
60 enum 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.
69 struct 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.
77 struct 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.
83 struct 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 
90 struct 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 
99 struct SequenceData {
100   SequenceData(std::vector<Stencil> Stencils) : Stencils(std::move(Stencils)) {}
101   std::vector<Stencil> Stencils;
102 };
103 
104 std::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 
114 std::string toStringData(const DebugPrintNodeData &Data) {
115   return (llvm::Twine("dPrint(\"") + Data.Id + "\")").str();
116 }
117 
118 std::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 
140 std::string toStringData(const SelectorData &) { return "selection(...)"; }
141 
142 std::string toStringData(const AccessData &Data) {
143   return (llvm::Twine("access(\"") + Data.BaseId + "\", " +
144           Data.Member->toString() + ")")
145       .str();
146 }
147 
148 std::string toStringData(const IfBoundData &Data) {
149   return (llvm::Twine("ifBound(\"") + Data.Id + "\", " +
150           Data.TrueStencil->toString() + ", " + Data.FalseStencil->toString() +
151           ")")
152       .str();
153 }
154 
155 std::string toStringData(const MatchConsumer<std::string> &) {
156   return "run(...)";
157 }
158 
159 std::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 
171 Error evalData(const RawTextData &Data, const MatchFinder::MatchResult &,
172                std::string *Result) {
173   Result->append(Data.Text);
174   return Error::success();
175 }
176 
177 Error 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 
189 Error 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 
229 Error 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 
265 Error 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 
285 Error 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 
292 Error 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 
301 Error 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 
309 template <typename T> class StencilImpl : public StencilInterface {
310   T Data;
311 
312 public:
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 
325 Stencil transformer::detail::makeStencil(StringRef Text) {
326   return std::make_shared<StencilImpl<RawTextData>>(std::string(Text));
327 }
328 
329 Stencil transformer::detail::makeStencil(RangeSelector Selector) {
330   return std::make_shared<StencilImpl<SelectorData>>(std::move(Selector));
331 }
332 
333 Stencil transformer::dPrint(StringRef Id) {
334   return std::make_shared<StencilImpl<DebugPrintNodeData>>(std::string(Id));
335 }
336 
337 Stencil transformer::expression(llvm::StringRef Id) {
338   return std::make_shared<StencilImpl<UnaryOperationData>>(
339       UnaryNodeOperator::Parens, std::string(Id));
340 }
341 
342 Stencil transformer::deref(llvm::StringRef ExprId) {
343   return std::make_shared<StencilImpl<UnaryOperationData>>(
344       UnaryNodeOperator::Deref, std::string(ExprId));
345 }
346 
347 Stencil transformer::maybeDeref(llvm::StringRef ExprId) {
348   return std::make_shared<StencilImpl<UnaryOperationData>>(
349       UnaryNodeOperator::MaybeDeref, std::string(ExprId));
350 }
351 
352 Stencil transformer::addressOf(llvm::StringRef ExprId) {
353   return std::make_shared<StencilImpl<UnaryOperationData>>(
354       UnaryNodeOperator::AddressOf, std::string(ExprId));
355 }
356 
357 Stencil transformer::maybeAddressOf(llvm::StringRef ExprId) {
358   return std::make_shared<StencilImpl<UnaryOperationData>>(
359       UnaryNodeOperator::MaybeAddressOf, std::string(ExprId));
360 }
361 
362 Stencil transformer::access(StringRef BaseId, Stencil Member) {
363   return std::make_shared<StencilImpl<AccessData>>(BaseId, std::move(Member));
364 }
365 
366 Stencil 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 
372 Stencil transformer::run(MatchConsumer<std::string> Fn) {
373   return std::make_shared<StencilImpl<MatchConsumer<std::string>>>(
374       std::move(Fn));
375 }
376 
377 Stencil 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