| //===--- Stencil.h - Stencil class ------------------------------*- C++ -*-===// |
| // |
| // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. |
| // See https://llvm.org/LICENSE.txt for license information. |
| // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception |
| // |
| //===----------------------------------------------------------------------===// |
| /// |
| /// \file |
| /// This file defines the *Stencil* abstraction: a code-generating object, |
| /// parameterized by named references to (bound) AST nodes. Given a match |
| /// result, a stencil can be evaluated to a string of source code. |
| /// |
| /// A stencil is similar in spirit to a format string: it is composed of a |
| /// series of raw text strings, references to nodes (the parameters) and helper |
| /// code-generation operations. |
| /// |
| //===----------------------------------------------------------------------===// |
| |
| #ifndef LLVM_CLANG_TOOLING_TRANSFORMER_STENCIL_H_ |
| #define LLVM_CLANG_TOOLING_TRANSFORMER_STENCIL_H_ |
| |
| #include "clang/AST/ASTContext.h" |
| #include "clang/AST/ASTTypeTraits.h" |
| #include "clang/ASTMatchers/ASTMatchFinder.h" |
| #include "clang/Tooling/Transformer/MatchConsumer.h" |
| #include "clang/Tooling/Transformer/RangeSelector.h" |
| #include "llvm/ADT/StringRef.h" |
| #include "llvm/Support/Error.h" |
| #include <string> |
| #include <vector> |
| |
| namespace clang { |
| namespace transformer { |
| /// A stencil is represented as a sequence of "parts" that can each individually |
| /// generate a code string based on a match result. The different kinds of |
| /// parts include (raw) text, references to bound nodes and assorted operations |
| /// on bound nodes. |
| /// |
| /// Users can create custom Stencil operations by implementing this interface. |
| class StencilPartInterface { |
| public: |
| virtual ~StencilPartInterface() = default; |
| |
| /// Evaluates this part to a string and appends it to \c Result. \c Result is |
| /// undefined in the case of an error. |
| virtual llvm::Error eval(const ast_matchers::MatchFinder::MatchResult &Match, |
| std::string *Result) const = 0; |
| |
| /// Constructs a string representation of the StencilPart. StencilParts |
| /// generated by the `selection` and `run` functions do not have a unique |
| /// string representation. |
| virtual std::string toString() const = 0; |
| |
| protected: |
| StencilPartInterface() = default; |
| |
| // Since this is an abstract class, copying/assigning only make sense for |
| // derived classes implementing `clone()`. |
| StencilPartInterface(const StencilPartInterface &) = default; |
| StencilPartInterface &operator=(const StencilPartInterface &) = default; |
| }; |
| |
| /// A copyable facade for a std::unique_ptr<StencilPartInterface>. Copies result |
| /// in a copy of the underlying pointee object. |
| class StencilPart { |
| public: |
| explicit StencilPart(std::shared_ptr<StencilPartInterface> Impl) |
| : Impl(std::move(Impl)) {} |
| |
| /// See `StencilPartInterface::eval()`. |
| llvm::Error eval(const ast_matchers::MatchFinder::MatchResult &Match, |
| std::string *Result) const { |
| return Impl->eval(Match, Result); |
| } |
| |
| std::string toString() const { |
| if (Impl == nullptr) |
| return ""; |
| return Impl->toString(); |
| } |
| |
| private: |
| std::shared_ptr<StencilPartInterface> Impl; |
| }; |
| |
| /// A sequence of code fragments, references to parameters and code-generation |
| /// operations that together can be evaluated to (a fragment of) source code, |
| /// given a match result. |
| class Stencil { |
| public: |
| Stencil() = default; |
| |
| /// Composes a stencil from a series of parts. |
| template <typename... Ts> static Stencil cat(Ts &&... Parts) { |
| Stencil S; |
| S.Parts = {wrap(std::forward<Ts>(Parts))...}; |
| return S; |
| } |
| |
| /// Appends data from a \p OtherStencil to this stencil. |
| void append(Stencil OtherStencil); |
| |
| // Evaluates the stencil given a match result. Requires that the nodes in the |
| // result includes any ids referenced in the stencil. References to missing |
| // nodes will result in an invalid_argument error. |
| llvm::Expected<std::string> |
| eval(const ast_matchers::MatchFinder::MatchResult &Match) const; |
| |
| // Allow Stencils to operate as std::function, for compatibility with |
| // Transformer's TextGenerator. |
| llvm::Expected<std::string> |
| operator()(const ast_matchers::MatchFinder::MatchResult &Result) const { |
| return eval(Result); |
| } |
| |
| /// Constructs a string representation of the Stencil. The string is not |
| /// guaranteed to be unique. |
| std::string toString() const { |
| std::vector<std::string> PartStrings; |
| PartStrings.reserve(Parts.size()); |
| for (const auto &Part : Parts) |
| PartStrings.push_back(Part.toString()); |
| return llvm::join(PartStrings, ", "); |
| } |
| |
| private: |
| static StencilPart wrap(llvm::StringRef Text); |
| static StencilPart wrap(RangeSelector Selector); |
| static StencilPart wrap(StencilPart Part) { return Part; } |
| |
| std::vector<StencilPart> Parts; |
| }; |
| |
| // |
| // Functions for conveniently building stencils. |
| // |
| |
| /// Convenience wrapper for Stencil::cat that can be imported with a using decl. |
| template <typename... Ts> Stencil cat(Ts &&... Parts) { |
| return Stencil::cat(std::forward<Ts>(Parts)...); |
| } |
| |
| /// \returns exactly the text provided. |
| StencilPart text(llvm::StringRef Text); |
| |
| /// \returns the source corresponding to the selected range. |
| StencilPart selection(RangeSelector Selector); |
| |
| /// Generates the source of the expression bound to \p Id, wrapping it in |
| /// parentheses if it may parse differently depending on context. For example, a |
| /// binary operation is always wrapped, while a variable reference is never |
| /// wrapped. |
| StencilPart expression(llvm::StringRef Id); |
| |
| /// Constructs an idiomatic dereferencing of the expression bound to \p ExprId. |
| /// \p ExprId is wrapped in parentheses, if needed. |
| StencilPart deref(llvm::StringRef ExprId); |
| |
| /// Constructs an expression that idiomatically takes the address of the |
| /// expression bound to \p ExprId. \p ExprId is wrapped in parentheses, if |
| /// needed. |
| StencilPart addressOf(llvm::StringRef ExprId); |
| |
| /// Constructs a `MemberExpr` that accesses the named member (\p Member) of the |
| /// object bound to \p BaseId. The access is constructed idiomatically: if \p |
| /// BaseId is bound to `e` and \p Member identifies member `m`, then returns |
| /// `e->m`, when e is a pointer, `e2->m` when e = `*e2` and `e.m` otherwise. |
| /// Additionally, `e` is wrapped in parentheses, if needed. |
| StencilPart access(llvm::StringRef BaseId, StencilPart Member); |
| inline StencilPart access(llvm::StringRef BaseId, llvm::StringRef Member) { |
| return access(BaseId, text(Member)); |
| } |
| |
| /// Chooses between the two stencil parts, based on whether \p ID is bound in |
| /// the match. |
| StencilPart ifBound(llvm::StringRef Id, StencilPart TruePart, |
| StencilPart FalsePart); |
| |
| /// Chooses between the two strings, based on whether \p ID is bound in the |
| /// match. |
| inline StencilPart ifBound(llvm::StringRef Id, llvm::StringRef TrueText, |
| llvm::StringRef FalseText) { |
| return ifBound(Id, text(TrueText), text(FalseText)); |
| } |
| |
| /// Wraps a MatchConsumer in a StencilPart, so that it can be used in a Stencil. |
| /// This supports user-defined extensions to the Stencil language. |
| StencilPart run(MatchConsumer<std::string> C); |
| |
| /// For debug use only; semantics are not guaranteed. |
| /// |
| /// \returns the string resulting from calling the node's print() method. |
| StencilPart dPrint(llvm::StringRef Id); |
| } // namespace transformer |
| |
| namespace tooling { |
| // DEPRECATED: These are temporary aliases supporting client migration to the |
| // `transformer` namespace. |
| using Stencil = transformer::Stencil; |
| using StencilPart = transformer::StencilPart; |
| namespace stencil { |
| using transformer::access; |
| using transformer::addressOf; |
| using transformer::cat; |
| using transformer::deref; |
| using transformer::dPrint; |
| using transformer::expression; |
| using transformer::ifBound; |
| using transformer::run; |
| using transformer::selection; |
| using transformer::text; |
| /// \returns the source corresponding to the identified node. |
| /// FIXME: Deprecated. Write `selection(node(Id))` instead. |
| inline transformer::StencilPart node(llvm::StringRef Id) { |
| return selection(tooling::node(Id)); |
| } |
| } // namespace stencil |
| } // namespace tooling |
| } // namespace clang |
| #endif // LLVM_CLANG_TOOLING_TRANSFORMER_STENCIL_H_ |