Revert "[lldb][test] Remove compiler version check and use regex" (#124101)
[llvm-project.git] / clang / lib / Tooling / Transformer / Stencil.cpp
blobc39a71b5983fc318fdc33c7752cb8294a0763208
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 //===----------------------------------------------------------------------===//
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/Basic/SourceLocation.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 "llvm/Support/Error.h"
22 #include <atomic>
23 #include <memory>
24 #include <string>
26 using namespace clang;
27 using namespace transformer;
29 using ast_matchers::BoundNodes;
30 using ast_matchers::MatchFinder;
31 using llvm::errc;
32 using llvm::Error;
33 using llvm::Expected;
34 using llvm::StringError;
36 static llvm::Expected<DynTypedNode> getNode(const BoundNodes &Nodes,
37 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;
46 static Error printNode(StringRef Id, const MatchFinder::MatchResult &Match,
47 std::string *Result) {
48 std::string Output;
49 llvm::raw_string_ostream Os(Output);
50 auto NodeOrErr = getNode(Match.Nodes, Id);
51 if (auto Err = NodeOrErr.takeError())
52 return Err;
53 const PrintingPolicy PP(Match.Context->getLangOpts());
54 if (const auto *ND = NodeOrErr->get<NamedDecl>()) {
55 // For NamedDecls, we can do a better job than printing the whole thing.
56 ND->getNameForDiagnostic(Os, PP, false);
57 } else {
58 NodeOrErr->print(Os, PP);
60 *Result += Output;
61 return Error::success();
64 namespace {
65 // An arbitrary fragment of code within a stencil.
66 class RawTextStencil : public StencilInterface {
67 std::string Text;
69 public:
70 explicit RawTextStencil(std::string T) : Text(std::move(T)) {}
72 std::string toString() const override {
73 std::string Result;
74 llvm::raw_string_ostream OS(Result);
75 OS << "\"";
76 OS.write_escaped(Text);
77 OS << "\"";
78 return Result;
81 Error eval(const MatchFinder::MatchResult &Match,
82 std::string *Result) const override {
83 Result->append(Text);
84 return Error::success();
88 // A debugging operation to dump the AST for a particular (bound) AST node.
89 class DebugPrintNodeStencil : public StencilInterface {
90 std::string Id;
92 public:
93 explicit DebugPrintNodeStencil(std::string S) : Id(std::move(S)) {}
95 std::string toString() const override {
96 return (llvm::Twine("dPrint(\"") + Id + "\")").str();
99 Error eval(const MatchFinder::MatchResult &Match,
100 std::string *Result) const override {
101 return printNode(Id, Match, Result);
105 // Operators that take a single node Id as an argument.
106 enum class UnaryNodeOperator {
107 Parens,
108 Deref,
109 MaybeDeref,
110 AddressOf,
111 MaybeAddressOf,
112 Describe,
115 // Generic container for stencil operations with a (single) node-id argument.
116 class UnaryOperationStencil : public StencilInterface {
117 UnaryNodeOperator Op;
118 std::string Id;
120 public:
121 UnaryOperationStencil(UnaryNodeOperator Op, std::string Id)
122 : Op(Op), Id(std::move(Id)) {}
124 std::string toString() const override {
125 StringRef OpName;
126 switch (Op) {
127 case UnaryNodeOperator::Parens:
128 OpName = "expression";
129 break;
130 case UnaryNodeOperator::Deref:
131 OpName = "deref";
132 break;
133 case UnaryNodeOperator::MaybeDeref:
134 OpName = "maybeDeref";
135 break;
136 case UnaryNodeOperator::AddressOf:
137 OpName = "addressOf";
138 break;
139 case UnaryNodeOperator::MaybeAddressOf:
140 OpName = "maybeAddressOf";
141 break;
142 case UnaryNodeOperator::Describe:
143 OpName = "describe";
144 break;
146 return (OpName + "(\"" + Id + "\")").str();
149 Error eval(const MatchFinder::MatchResult &Match,
150 std::string *Result) const override {
151 // The `Describe` operation can be applied to any node, not just
152 // expressions, so it is handled here, separately.
153 if (Op == UnaryNodeOperator::Describe)
154 return printNode(Id, Match, Result);
156 const auto *E = Match.Nodes.getNodeAs<Expr>(Id);
157 if (E == nullptr)
158 return llvm::make_error<StringError>(errc::invalid_argument,
159 "Id not bound or not Expr: " + Id);
160 std::optional<std::string> Source;
161 switch (Op) {
162 case UnaryNodeOperator::Parens:
163 Source = tooling::buildParens(*E, *Match.Context);
164 break;
165 case UnaryNodeOperator::Deref:
166 Source = tooling::buildDereference(*E, *Match.Context);
167 break;
168 case UnaryNodeOperator::MaybeDeref:
169 if (E->getType()->isAnyPointerType() ||
170 tooling::isKnownPointerLikeType(E->getType(), *Match.Context)) {
171 // Strip off any operator->. This can only occur inside an actual arrow
172 // member access, so we treat it as equivalent to an actual object
173 // expression.
174 if (const auto *OpCall = dyn_cast<clang::CXXOperatorCallExpr>(E)) {
175 if (OpCall->getOperator() == clang::OO_Arrow &&
176 OpCall->getNumArgs() == 1) {
177 E = OpCall->getArg(0);
180 Source = tooling::buildDereference(*E, *Match.Context);
181 break;
183 *Result += tooling::getText(*E, *Match.Context);
184 return Error::success();
185 case UnaryNodeOperator::AddressOf:
186 Source = tooling::buildAddressOf(*E, *Match.Context);
187 break;
188 case UnaryNodeOperator::MaybeAddressOf:
189 if (E->getType()->isAnyPointerType() ||
190 tooling::isKnownPointerLikeType(E->getType(), *Match.Context)) {
191 // Strip off any operator->. This can only occur inside an actual arrow
192 // member access, so we treat it as equivalent to an actual object
193 // expression.
194 if (const auto *OpCall = dyn_cast<clang::CXXOperatorCallExpr>(E)) {
195 if (OpCall->getOperator() == clang::OO_Arrow &&
196 OpCall->getNumArgs() == 1) {
197 E = OpCall->getArg(0);
200 *Result += tooling::getText(*E, *Match.Context);
201 return Error::success();
203 Source = tooling::buildAddressOf(*E, *Match.Context);
204 break;
205 case UnaryNodeOperator::Describe:
206 llvm_unreachable("This case is handled at the start of the function");
208 if (!Source)
209 return llvm::make_error<StringError>(
210 errc::invalid_argument,
211 "Could not construct expression source from ID: " + Id);
212 *Result += *Source;
213 return Error::success();
217 // The fragment of code corresponding to the selected range.
218 class SelectorStencil : public StencilInterface {
219 RangeSelector Selector;
221 public:
222 explicit SelectorStencil(RangeSelector S) : Selector(std::move(S)) {}
224 std::string toString() const override { return "selection(...)"; }
226 Error eval(const MatchFinder::MatchResult &Match,
227 std::string *Result) const override {
228 auto RawRange = Selector(Match);
229 if (!RawRange)
230 return RawRange.takeError();
231 CharSourceRange Range = Lexer::makeFileCharRange(
232 *RawRange, *Match.SourceManager, Match.Context->getLangOpts());
233 if (Range.isInvalid()) {
234 // Validate the original range to attempt to get a meaningful error
235 // message. If it's valid, then something else is the cause and we just
236 // return the generic failure message.
237 if (auto Err = tooling::validateRange(*RawRange, *Match.SourceManager,
238 /*AllowSystemHeaders=*/true))
239 return handleErrors(std::move(Err), [](std::unique_ptr<StringError> E) {
240 assert(E->convertToErrorCode() ==
241 llvm::make_error_code(errc::invalid_argument) &&
242 "Validation errors must carry the invalid_argument code");
243 return llvm::createStringError(
244 errc::invalid_argument,
245 "selected range could not be resolved to a valid source range; " +
246 E->getMessage());
248 return llvm::createStringError(
249 errc::invalid_argument,
250 "selected range could not be resolved to a valid source range");
252 // Validate `Range`, because `makeFileCharRange` accepts some ranges that
253 // `validateRange` rejects.
254 if (auto Err = tooling::validateRange(Range, *Match.SourceManager,
255 /*AllowSystemHeaders=*/true))
256 return joinErrors(
257 llvm::createStringError(errc::invalid_argument,
258 "selected range is not valid for editing"),
259 std::move(Err));
260 *Result += tooling::getText(Range, *Match.Context);
261 return Error::success();
265 // A stencil operation to build a member access `e.m` or `e->m`, as appropriate.
266 class AccessStencil : public StencilInterface {
267 std::string BaseId;
268 Stencil Member;
270 public:
271 AccessStencil(StringRef BaseId, Stencil Member)
272 : BaseId(std::string(BaseId)), Member(std::move(Member)) {}
274 std::string toString() const override {
275 return (llvm::Twine("access(\"") + BaseId + "\", " + Member->toString() +
276 ")")
277 .str();
280 Error eval(const MatchFinder::MatchResult &Match,
281 std::string *Result) const override {
282 const auto *E = Match.Nodes.getNodeAs<Expr>(BaseId);
283 if (E == nullptr)
284 return llvm::make_error<StringError>(errc::invalid_argument,
285 "Id not bound: " + BaseId);
286 std::optional<std::string> S = tooling::buildAccess(*E, *Match.Context);
287 if (!S)
288 return llvm::make_error<StringError>(
289 errc::invalid_argument,
290 "Could not construct object text from ID: " + BaseId);
291 *Result += *S;
292 return Member->eval(Match, Result);
296 class IfBoundStencil : public StencilInterface {
297 std::string Id;
298 Stencil TrueStencil;
299 Stencil FalseStencil;
301 public:
302 IfBoundStencil(StringRef Id, Stencil TrueStencil, Stencil FalseStencil)
303 : Id(std::string(Id)), TrueStencil(std::move(TrueStencil)),
304 FalseStencil(std::move(FalseStencil)) {}
306 std::string toString() const override {
307 return (llvm::Twine("ifBound(\"") + Id + "\", " + TrueStencil->toString() +
308 ", " + FalseStencil->toString() + ")")
309 .str();
312 Error eval(const MatchFinder::MatchResult &Match,
313 std::string *Result) const override {
314 auto &M = Match.Nodes.getMap();
315 return (M.find(Id) != M.end() ? TrueStencil : FalseStencil)
316 ->eval(Match, Result);
320 class SelectBoundStencil : public clang::transformer::StencilInterface {
321 static bool containsNoNullStencils(
322 const std::vector<std::pair<std::string, Stencil>> &Cases) {
323 for (const auto &S : Cases)
324 if (S.second == nullptr)
325 return false;
326 return true;
329 public:
330 SelectBoundStencil(std::vector<std::pair<std::string, Stencil>> Cases,
331 Stencil Default)
332 : CaseStencils(std::move(Cases)), DefaultStencil(std::move(Default)) {
333 assert(containsNoNullStencils(CaseStencils) &&
334 "cases of selectBound may not be null");
336 ~SelectBoundStencil() override {}
338 llvm::Error eval(const MatchFinder::MatchResult &match,
339 std::string *result) const override {
340 const BoundNodes::IDToNodeMap &NodeMap = match.Nodes.getMap();
341 for (const auto &S : CaseStencils) {
342 if (NodeMap.count(S.first) > 0) {
343 return S.second->eval(match, result);
347 if (DefaultStencil != nullptr) {
348 return DefaultStencil->eval(match, result);
351 llvm::SmallVector<llvm::StringRef, 2> CaseIDs;
352 CaseIDs.reserve(CaseStencils.size());
353 for (const auto &S : CaseStencils)
354 CaseIDs.emplace_back(S.first);
356 return llvm::createStringError(
357 errc::result_out_of_range,
358 llvm::Twine("selectBound failed: no cases bound and no default: {") +
359 llvm::join(CaseIDs, ", ") + "}");
362 std::string toString() const override {
363 std::string Buffer;
364 llvm::raw_string_ostream Stream(Buffer);
365 Stream << "selectBound({";
366 bool First = true;
367 for (const auto &S : CaseStencils) {
368 if (First)
369 First = false;
370 else
371 Stream << "}, ";
372 Stream << "{\"" << S.first << "\", " << S.second->toString();
374 Stream << "}}";
375 if (DefaultStencil != nullptr) {
376 Stream << ", " << DefaultStencil->toString();
378 Stream << ")";
379 return Buffer;
382 private:
383 std::vector<std::pair<std::string, Stencil>> CaseStencils;
384 Stencil DefaultStencil;
387 class SequenceStencil : public StencilInterface {
388 std::vector<Stencil> Stencils;
390 public:
391 SequenceStencil(std::vector<Stencil> Stencils)
392 : Stencils(std::move(Stencils)) {}
394 std::string toString() const override {
395 llvm::SmallVector<std::string, 2> Parts;
396 Parts.reserve(Stencils.size());
397 for (const auto &S : Stencils)
398 Parts.push_back(S->toString());
399 return (llvm::Twine("seq(") + llvm::join(Parts, ", ") + ")").str();
402 Error eval(const MatchFinder::MatchResult &Match,
403 std::string *Result) const override {
404 for (const auto &S : Stencils)
405 if (auto Err = S->eval(Match, Result))
406 return Err;
407 return Error::success();
411 class RunStencil : public StencilInterface {
412 MatchConsumer<std::string> Consumer;
414 public:
415 explicit RunStencil(MatchConsumer<std::string> C) : Consumer(std::move(C)) {}
417 std::string toString() const override { return "run(...)"; }
419 Error eval(const MatchFinder::MatchResult &Match,
420 std::string *Result) const override {
422 Expected<std::string> Value = Consumer(Match);
423 if (!Value)
424 return Value.takeError();
425 *Result += *Value;
426 return Error::success();
429 } // namespace
431 Stencil transformer::detail::makeStencil(StringRef Text) {
432 return std::make_shared<RawTextStencil>(std::string(Text));
435 Stencil transformer::detail::makeStencil(RangeSelector Selector) {
436 return std::make_shared<SelectorStencil>(std::move(Selector));
439 Stencil transformer::dPrint(StringRef Id) {
440 return std::make_shared<DebugPrintNodeStencil>(std::string(Id));
443 Stencil transformer::expression(llvm::StringRef Id) {
444 return std::make_shared<UnaryOperationStencil>(UnaryNodeOperator::Parens,
445 std::string(Id));
448 Stencil transformer::deref(llvm::StringRef ExprId) {
449 return std::make_shared<UnaryOperationStencil>(UnaryNodeOperator::Deref,
450 std::string(ExprId));
453 Stencil transformer::maybeDeref(llvm::StringRef ExprId) {
454 return std::make_shared<UnaryOperationStencil>(UnaryNodeOperator::MaybeDeref,
455 std::string(ExprId));
458 Stencil transformer::addressOf(llvm::StringRef ExprId) {
459 return std::make_shared<UnaryOperationStencil>(UnaryNodeOperator::AddressOf,
460 std::string(ExprId));
463 Stencil transformer::maybeAddressOf(llvm::StringRef ExprId) {
464 return std::make_shared<UnaryOperationStencil>(
465 UnaryNodeOperator::MaybeAddressOf, std::string(ExprId));
468 Stencil transformer::describe(StringRef Id) {
469 return std::make_shared<UnaryOperationStencil>(UnaryNodeOperator::Describe,
470 std::string(Id));
473 Stencil transformer::access(StringRef BaseId, Stencil Member) {
474 return std::make_shared<AccessStencil>(BaseId, std::move(Member));
477 Stencil transformer::ifBound(StringRef Id, Stencil TrueStencil,
478 Stencil FalseStencil) {
479 return std::make_shared<IfBoundStencil>(Id, std::move(TrueStencil),
480 std::move(FalseStencil));
483 Stencil transformer::selectBound(
484 std::vector<std::pair<std::string, Stencil>> CaseStencils,
485 Stencil DefaultStencil) {
486 return std::make_shared<SelectBoundStencil>(std::move(CaseStencils),
487 std::move(DefaultStencil));
490 Stencil transformer::run(MatchConsumer<std::string> Fn) {
491 return std::make_shared<RunStencil>(std::move(Fn));
494 Stencil transformer::catVector(std::vector<Stencil> Parts) {
495 // Only one argument, so don't wrap in sequence.
496 if (Parts.size() == 1)
497 return std::move(Parts[0]);
498 return std::make_shared<SequenceStencil>(std::move(Parts));