/Users/buildslave/jenkins/workspace/coverage/llvm-project/clang/lib/Tooling/Transformer/Stencil.cpp
Line | Count | Source (jump to first uncovered line) |
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/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> |
25 | | |
26 | | using namespace clang; |
27 | | using namespace transformer; |
28 | | |
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; |
35 | | |
36 | | static llvm::Expected<DynTypedNode> getNode(const BoundNodes &Nodes, |
37 | 6 | StringRef Id) { |
38 | 6 | auto &NodesMap = Nodes.getMap(); |
39 | 6 | auto It = NodesMap.find(Id); |
40 | 6 | if (It == NodesMap.end()) |
41 | 0 | return llvm::make_error<llvm::StringError>(llvm::errc::invalid_argument, |
42 | 0 | "Id not bound: " + Id); |
43 | 6 | return It->second; |
44 | 6 | } |
45 | | |
46 | | static Error printNode(StringRef Id, const MatchFinder::MatchResult &Match, |
47 | 6 | std::string *Result) { |
48 | 6 | std::string Output; |
49 | 6 | llvm::raw_string_ostream Os(Output); |
50 | 6 | auto NodeOrErr = getNode(Match.Nodes, Id); |
51 | 6 | if (auto Err = NodeOrErr.takeError()) |
52 | 0 | return Err; |
53 | 6 | NodeOrErr->print(Os, PrintingPolicy(Match.Context->getLangOpts())); |
54 | 6 | *Result += Os.str(); |
55 | 6 | return Error::success(); |
56 | 6 | } |
57 | | |
58 | | namespace { |
59 | | // An arbitrary fragment of code within a stencil. |
60 | | class RawTextStencil : public StencilInterface { |
61 | | std::string Text; |
62 | | |
63 | | public: |
64 | 156 | explicit RawTextStencil(std::string T) : Text(std::move(T)) {} |
65 | | |
66 | 24 | std::string toString() const override { |
67 | 24 | std::string Result; |
68 | 24 | llvm::raw_string_ostream OS(Result); |
69 | 24 | OS << "\""; |
70 | 24 | OS.write_escaped(Text); |
71 | 24 | OS << "\""; |
72 | 24 | OS.flush(); |
73 | 24 | return Result; |
74 | 24 | } |
75 | | |
76 | | Error eval(const MatchFinder::MatchResult &Match, |
77 | 176 | std::string *Result) const override { |
78 | 176 | Result->append(Text); |
79 | 176 | return Error::success(); |
80 | 176 | } |
81 | | }; |
82 | | |
83 | | // A debugging operation to dump the AST for a particular (bound) AST node. |
84 | | class DebugPrintNodeStencil : public StencilInterface { |
85 | | std::string Id; |
86 | | |
87 | | public: |
88 | 1 | explicit DebugPrintNodeStencil(std::string S) : Id(std::move(S)) {} |
89 | | |
90 | 1 | std::string toString() const override { |
91 | 1 | return (llvm::Twine("dPrint(\"") + Id + "\")").str(); |
92 | 1 | } |
93 | | |
94 | | Error eval(const MatchFinder::MatchResult &Match, |
95 | 0 | std::string *Result) const override { |
96 | 0 | return printNode(Id, Match, Result); |
97 | 0 | } |
98 | | }; |
99 | | |
100 | | // Operators that take a single node Id as an argument. |
101 | | enum class UnaryNodeOperator { |
102 | | Parens, |
103 | | Deref, |
104 | | MaybeDeref, |
105 | | AddressOf, |
106 | | MaybeAddressOf, |
107 | | Describe, |
108 | | }; |
109 | | |
110 | | // Generic container for stencil operations with a (single) node-id argument. |
111 | | class UnaryOperationStencil : public StencilInterface { |
112 | | UnaryNodeOperator Op; |
113 | | std::string Id; |
114 | | |
115 | | public: |
116 | | UnaryOperationStencil(UnaryNodeOperator Op, std::string Id) |
117 | 32 | : Op(Op), Id(std::move(Id)) {} |
118 | | |
119 | 4 | std::string toString() const override { |
120 | 4 | StringRef OpName; |
121 | 4 | switch (Op) { |
122 | 1 | case UnaryNodeOperator::Parens: |
123 | 1 | OpName = "expression"; |
124 | 1 | break; |
125 | 1 | case UnaryNodeOperator::Deref: |
126 | 1 | OpName = "deref"; |
127 | 1 | break; |
128 | 0 | case UnaryNodeOperator::MaybeDeref: |
129 | 0 | OpName = "maybeDeref"; |
130 | 0 | break; |
131 | 1 | case UnaryNodeOperator::AddressOf: |
132 | 1 | OpName = "addressOf"; |
133 | 1 | break; |
134 | 0 | case UnaryNodeOperator::MaybeAddressOf: |
135 | 0 | OpName = "maybeAddressOf"; |
136 | 0 | break; |
137 | 1 | case UnaryNodeOperator::Describe: |
138 | 1 | OpName = "describe"; |
139 | 1 | break; |
140 | 4 | } |
141 | 4 | return (OpName + "(\"" + Id + "\")").str(); |
142 | 4 | } |
143 | | |
144 | | Error eval(const MatchFinder::MatchResult &Match, |
145 | 28 | std::string *Result) const override { |
146 | | // The `Describe` operation can be applied to any node, not just |
147 | | // expressions, so it is handled here, separately. |
148 | 28 | if (Op == UnaryNodeOperator::Describe) |
149 | 6 | return printNode(Id, Match, Result); |
150 | | |
151 | 22 | const auto *E = Match.Nodes.getNodeAs<Expr>(Id); |
152 | 22 | if (E == nullptr) |
153 | 1 | return llvm::make_error<StringError>(errc::invalid_argument, |
154 | 1 | "Id not bound or not Expr: " + Id); |
155 | 21 | llvm::Optional<std::string> Source; |
156 | 21 | switch (Op) { |
157 | 3 | case UnaryNodeOperator::Parens: |
158 | 3 | Source = tooling::buildParens(*E, *Match.Context); |
159 | 3 | break; |
160 | 3 | case UnaryNodeOperator::Deref: |
161 | 3 | Source = tooling::buildDereference(*E, *Match.Context); |
162 | 3 | break; |
163 | 6 | case UnaryNodeOperator::MaybeDeref: |
164 | 6 | if (E->getType()->isAnyPointerType() || |
165 | 6 | tooling::isKnownPointerLikeType(E->getType(), *Match.Context)2 ) { |
166 | | // Strip off any operator->. This can only occur inside an actual arrow |
167 | | // member access, so we treat it as equivalent to an actual object |
168 | | // expression. |
169 | 5 | if (const auto *OpCall = dyn_cast<clang::CXXOperatorCallExpr>(E)) { |
170 | 1 | if (OpCall->getOperator() == clang::OO_Arrow && |
171 | 1 | OpCall->getNumArgs() == 1) { |
172 | 1 | E = OpCall->getArg(0); |
173 | 1 | } |
174 | 1 | } |
175 | 5 | Source = tooling::buildDereference(*E, *Match.Context); |
176 | 5 | break; |
177 | 5 | } |
178 | 1 | *Result += tooling::getText(*E, *Match.Context); |
179 | 1 | return Error::success(); |
180 | 4 | case UnaryNodeOperator::AddressOf: |
181 | 4 | Source = tooling::buildAddressOf(*E, *Match.Context); |
182 | 4 | break; |
183 | 5 | case UnaryNodeOperator::MaybeAddressOf: |
184 | 5 | if (E->getType()->isAnyPointerType() || |
185 | 5 | tooling::isKnownPointerLikeType(E->getType(), *Match.Context)3 ) { |
186 | | // Strip off any operator->. This can only occur inside an actual arrow |
187 | | // member access, so we treat it as equivalent to an actual object |
188 | | // expression. |
189 | 3 | if (const auto *OpCall = dyn_cast<clang::CXXOperatorCallExpr>(E)) { |
190 | 1 | if (OpCall->getOperator() == clang::OO_Arrow && |
191 | 1 | OpCall->getNumArgs() == 1) { |
192 | 1 | E = OpCall->getArg(0); |
193 | 1 | } |
194 | 1 | } |
195 | 3 | *Result += tooling::getText(*E, *Match.Context); |
196 | 3 | return Error::success(); |
197 | 3 | } |
198 | 2 | Source = tooling::buildAddressOf(*E, *Match.Context); |
199 | 2 | break; |
200 | 0 | case UnaryNodeOperator::Describe: |
201 | 0 | llvm_unreachable("This case is handled at the start of the function"); |
202 | 21 | } |
203 | 17 | if (!Source) |
204 | 0 | return llvm::make_error<StringError>( |
205 | 0 | errc::invalid_argument, |
206 | 0 | "Could not construct expression source from ID: " + Id); |
207 | 17 | *Result += *Source; |
208 | 17 | return Error::success(); |
209 | 17 | } |
210 | | }; |
211 | | |
212 | | // The fragment of code corresponding to the selected range. |
213 | | class SelectorStencil : public StencilInterface { |
214 | | RangeSelector Selector; |
215 | | |
216 | | public: |
217 | 19 | explicit SelectorStencil(RangeSelector S) : Selector(std::move(S)) {} |
218 | | |
219 | 2 | std::string toString() const override { return "selection(...)"; } |
220 | | |
221 | | Error eval(const MatchFinder::MatchResult &Match, |
222 | 44 | std::string *Result) const override { |
223 | 44 | auto RawRange = Selector(Match); |
224 | 44 | if (!RawRange) |
225 | 1 | return RawRange.takeError(); |
226 | 43 | CharSourceRange Range = Lexer::makeFileCharRange( |
227 | 43 | *RawRange, *Match.SourceManager, Match.Context->getLangOpts()); |
228 | 43 | if (Range.isInvalid()) { |
229 | | // Validate the original range to attempt to get a meaningful error |
230 | | // message. If it's valid, then something else is the cause and we just |
231 | | // return the generic failure message. |
232 | 3 | if (auto Err = |
233 | 3 | tooling::validateEditRange(*RawRange, *Match.SourceManager)) |
234 | 3 | return handleErrors(std::move(Err), [](std::unique_ptr<StringError> E) { |
235 | 3 | assert(E->convertToErrorCode() == |
236 | 3 | llvm::make_error_code(errc::invalid_argument) && |
237 | 3 | "Validation errors must carry the invalid_argument code"); |
238 | 0 | return llvm::createStringError( |
239 | 3 | errc::invalid_argument, |
240 | 3 | "selected range could not be resolved to a valid source range; " + |
241 | 3 | E->getMessage()); |
242 | 3 | }); |
243 | 0 | return llvm::createStringError( |
244 | 0 | errc::invalid_argument, |
245 | 0 | "selected range could not be resolved to a valid source range"); |
246 | 3 | } |
247 | | // Validate `Range`, because `makeFileCharRange` accepts some ranges that |
248 | | // `validateEditRange` rejects. |
249 | 40 | if (auto Err = tooling::validateEditRange(Range, *Match.SourceManager)) |
250 | 0 | return joinErrors( |
251 | 0 | llvm::createStringError(errc::invalid_argument, |
252 | 0 | "selected range is not valid for editing"), |
253 | 0 | std::move(Err)); |
254 | 40 | *Result += tooling::getText(Range, *Match.Context); |
255 | 40 | return Error::success(); |
256 | 40 | } |
257 | | }; |
258 | | |
259 | | // A stencil operation to build a member access `e.m` or `e->m`, as appropriate. |
260 | | class AccessStencil : public StencilInterface { |
261 | | std::string BaseId; |
262 | | Stencil Member; |
263 | | |
264 | | public: |
265 | | AccessStencil(StringRef BaseId, Stencil Member) |
266 | 18 | : BaseId(std::string(BaseId)), Member(std::move(Member)) {} |
267 | | |
268 | 8 | std::string toString() const override { |
269 | 8 | return (llvm::Twine("access(\"") + BaseId + "\", " + Member->toString() + |
270 | 8 | ")") |
271 | 8 | .str(); |
272 | 8 | } |
273 | | |
274 | | Error eval(const MatchFinder::MatchResult &Match, |
275 | 10 | std::string *Result) const override { |
276 | 10 | const auto *E = Match.Nodes.getNodeAs<Expr>(BaseId); |
277 | 10 | if (E == nullptr) |
278 | 0 | return llvm::make_error<StringError>(errc::invalid_argument, |
279 | 0 | "Id not bound: " + BaseId); |
280 | 10 | llvm::Optional<std::string> S = tooling::buildAccess(*E, *Match.Context); |
281 | 10 | if (!S) |
282 | 0 | return llvm::make_error<StringError>( |
283 | 0 | errc::invalid_argument, |
284 | 0 | "Could not construct object text from ID: " + BaseId); |
285 | 10 | *Result += *S; |
286 | 10 | return Member->eval(Match, Result); |
287 | 10 | } |
288 | | }; |
289 | | |
290 | | class IfBoundStencil : public StencilInterface { |
291 | | std::string Id; |
292 | | Stencil TrueStencil; |
293 | | Stencil FalseStencil; |
294 | | |
295 | | public: |
296 | | IfBoundStencil(StringRef Id, Stencil TrueStencil, Stencil FalseStencil) |
297 | | : Id(std::string(Id)), TrueStencil(std::move(TrueStencil)), |
298 | 5 | FalseStencil(std::move(FalseStencil)) {} |
299 | | |
300 | 3 | std::string toString() const override { |
301 | 3 | return (llvm::Twine("ifBound(\"") + Id + "\", " + TrueStencil->toString() + |
302 | 3 | ", " + FalseStencil->toString() + ")") |
303 | 3 | .str(); |
304 | 3 | } |
305 | | |
306 | | Error eval(const MatchFinder::MatchResult &Match, |
307 | 2 | std::string *Result) const override { |
308 | 2 | auto &M = Match.Nodes.getMap(); |
309 | 2 | return (M.find(Id) != M.end() ? TrueStencil1 : FalseStencil1 ) |
310 | 2 | ->eval(Match, Result); |
311 | 2 | } |
312 | | }; |
313 | | |
314 | | class SelectBoundStencil : public clang::transformer::StencilInterface { |
315 | | static bool containsNoNullStencils( |
316 | 7 | const std::vector<std::pair<std::string, Stencil>> &Cases) { |
317 | 7 | for (const auto &S : Cases) |
318 | 18 | if (S.second == nullptr) |
319 | 0 | return false; |
320 | 7 | return true; |
321 | 7 | } |
322 | | |
323 | | public: |
324 | | SelectBoundStencil(std::vector<std::pair<std::string, Stencil>> Cases, |
325 | | Stencil Default) |
326 | 7 | : CaseStencils(std::move(Cases)), DefaultStencil(std::move(Default)) { |
327 | 7 | assert(containsNoNullStencils(CaseStencils) && |
328 | 7 | "cases of selectBound may not be null"); |
329 | 7 | } |
330 | 7 | ~SelectBoundStencil() override{}; |
331 | | |
332 | | llvm::Error eval(const MatchFinder::MatchResult &match, |
333 | 4 | std::string *result) const override { |
334 | 4 | const BoundNodes::IDToNodeMap &NodeMap = match.Nodes.getMap(); |
335 | 8 | for (const auto &S : CaseStencils) { |
336 | 8 | if (NodeMap.count(S.first) > 0) { |
337 | 2 | return S.second->eval(match, result); |
338 | 2 | } |
339 | 8 | } |
340 | | |
341 | 2 | if (DefaultStencil != nullptr) { |
342 | 1 | return DefaultStencil->eval(match, result); |
343 | 1 | } |
344 | | |
345 | 1 | llvm::SmallVector<llvm::StringRef, 2> CaseIDs; |
346 | 1 | CaseIDs.reserve(CaseStencils.size()); |
347 | 1 | for (const auto &S : CaseStencils) |
348 | 4 | CaseIDs.emplace_back(S.first); |
349 | | |
350 | 1 | return llvm::createStringError( |
351 | 1 | errc::result_out_of_range, |
352 | 1 | llvm::Twine("selectBound failed: no cases bound and no default: {") + |
353 | 1 | llvm::join(CaseIDs, ", ") + "}"); |
354 | 2 | } |
355 | | |
356 | 3 | std::string toString() const override { |
357 | 3 | std::string Buffer; |
358 | 3 | llvm::raw_string_ostream Stream(Buffer); |
359 | 3 | Stream << "selectBound({"; |
360 | 3 | bool First = true; |
361 | 5 | for (const auto &S : CaseStencils) { |
362 | 5 | if (First) |
363 | 3 | First = false; |
364 | 2 | else |
365 | 2 | Stream << "}, "; |
366 | 5 | Stream << "{\"" << S.first << "\", " << S.second->toString(); |
367 | 5 | } |
368 | 3 | Stream << "}}"; |
369 | 3 | if (DefaultStencil != nullptr) { |
370 | 1 | Stream << ", " << DefaultStencil->toString(); |
371 | 1 | } |
372 | 3 | Stream << ")"; |
373 | 3 | return Stream.str(); |
374 | 3 | } |
375 | | |
376 | | private: |
377 | | std::vector<std::pair<std::string, Stencil>> CaseStencils; |
378 | | Stencil DefaultStencil; |
379 | | }; |
380 | | |
381 | | class SequenceStencil : public StencilInterface { |
382 | | std::vector<Stencil> Stencils; |
383 | | |
384 | | public: |
385 | | SequenceStencil(std::vector<Stencil> Stencils) |
386 | 11 | : Stencils(std::move(Stencils)) {} |
387 | | |
388 | 4 | std::string toString() const override { |
389 | 4 | llvm::SmallVector<std::string, 2> Parts; |
390 | 4 | Parts.reserve(Stencils.size()); |
391 | 4 | for (const auto &S : Stencils) |
392 | 10 | Parts.push_back(S->toString()); |
393 | 4 | return (llvm::Twine("seq(") + llvm::join(Parts, ", ") + ")").str(); |
394 | 4 | } |
395 | | |
396 | | Error eval(const MatchFinder::MatchResult &Match, |
397 | 24 | std::string *Result) const override { |
398 | 24 | for (const auto &S : Stencils) |
399 | 94 | if (auto Err = S->eval(Match, Result)) |
400 | 3 | return Err; |
401 | 21 | return Error::success(); |
402 | 24 | } |
403 | | }; |
404 | | |
405 | | class RunStencil : public StencilInterface { |
406 | | MatchConsumer<std::string> Consumer; |
407 | | |
408 | | public: |
409 | 2 | explicit RunStencil(MatchConsumer<std::string> C) : Consumer(std::move(C)) {} |
410 | | |
411 | 1 | std::string toString() const override { return "run(...)"; } |
412 | | |
413 | | Error eval(const MatchFinder::MatchResult &Match, |
414 | 1 | std::string *Result) const override { |
415 | | |
416 | 1 | Expected<std::string> Value = Consumer(Match); |
417 | 1 | if (!Value) |
418 | 0 | return Value.takeError(); |
419 | 1 | *Result += *Value; |
420 | 1 | return Error::success(); |
421 | 1 | } |
422 | | }; |
423 | | } // namespace |
424 | | |
425 | 156 | Stencil transformer::detail::makeStencil(StringRef Text) { |
426 | 156 | return std::make_shared<RawTextStencil>(std::string(Text)); |
427 | 156 | } |
428 | | |
429 | 19 | Stencil transformer::detail::makeStencil(RangeSelector Selector) { |
430 | 19 | return std::make_shared<SelectorStencil>(std::move(Selector)); |
431 | 19 | } |
432 | | |
433 | 1 | Stencil transformer::dPrint(StringRef Id) { |
434 | 1 | return std::make_shared<DebugPrintNodeStencil>(std::string(Id)); |
435 | 1 | } |
436 | | |
437 | 5 | Stencil transformer::expression(llvm::StringRef Id) { |
438 | 5 | return std::make_shared<UnaryOperationStencil>(UnaryNodeOperator::Parens, |
439 | 5 | std::string(Id)); |
440 | 5 | } |
441 | | |
442 | 4 | Stencil transformer::deref(llvm::StringRef ExprId) { |
443 | 4 | return std::make_shared<UnaryOperationStencil>(UnaryNodeOperator::Deref, |
444 | 4 | std::string(ExprId)); |
445 | 4 | } |
446 | | |
447 | 6 | Stencil transformer::maybeDeref(llvm::StringRef ExprId) { |
448 | 6 | return std::make_shared<UnaryOperationStencil>(UnaryNodeOperator::MaybeDeref, |
449 | 6 | std::string(ExprId)); |
450 | 6 | } |
451 | | |
452 | 5 | Stencil transformer::addressOf(llvm::StringRef ExprId) { |
453 | 5 | return std::make_shared<UnaryOperationStencil>(UnaryNodeOperator::AddressOf, |
454 | 5 | std::string(ExprId)); |
455 | 5 | } |
456 | | |
457 | 5 | Stencil transformer::maybeAddressOf(llvm::StringRef ExprId) { |
458 | 5 | return std::make_shared<UnaryOperationStencil>( |
459 | 5 | UnaryNodeOperator::MaybeAddressOf, std::string(ExprId)); |
460 | 5 | } |
461 | | |
462 | 7 | Stencil transformer::describe(StringRef Id) { |
463 | 7 | return std::make_shared<UnaryOperationStencil>(UnaryNodeOperator::Describe, |
464 | 7 | std::string(Id)); |
465 | 7 | } |
466 | | |
467 | 18 | Stencil transformer::access(StringRef BaseId, Stencil Member) { |
468 | 18 | return std::make_shared<AccessStencil>(BaseId, std::move(Member)); |
469 | 18 | } |
470 | | |
471 | | Stencil transformer::ifBound(StringRef Id, Stencil TrueStencil, |
472 | 5 | Stencil FalseStencil) { |
473 | 5 | return std::make_shared<IfBoundStencil>(Id, std::move(TrueStencil), |
474 | 5 | std::move(FalseStencil)); |
475 | 5 | } |
476 | | |
477 | | Stencil transformer::selectBound( |
478 | | std::vector<std::pair<std::string, Stencil>> CaseStencils, |
479 | 7 | Stencil DefaultStencil) { |
480 | 7 | return std::make_shared<SelectBoundStencil>(std::move(CaseStencils), |
481 | 7 | std::move(DefaultStencil)); |
482 | 7 | } |
483 | | |
484 | 2 | Stencil transformer::run(MatchConsumer<std::string> Fn) { |
485 | 2 | return std::make_shared<RunStencil>(std::move(Fn)); |
486 | 2 | } |
487 | | |
488 | 138 | Stencil transformer::catVector(std::vector<Stencil> Parts) { |
489 | | // Only one argument, so don't wrap in sequence. |
490 | 138 | if (Parts.size() == 1) |
491 | 127 | return std::move(Parts[0]); |
492 | 11 | return std::make_shared<SequenceStencil>(std::move(Parts)); |
493 | 138 | } |