//===--- 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 #include 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. Copies result /// in a copy of the underlying pointee object. class StencilPart { public: explicit StencilPart(std::shared_ptr 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 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 static Stencil cat(Ts &&... Parts) { Stencil S; S.Parts = {wrap(std::forward(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 eval(const ast_matchers::MatchFinder::MatchResult &Match) const; // Allow Stencils to operate as std::function, for compatibility with // Transformer's TextGenerator. llvm::Expected 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 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 Parts; }; // // Functions for conveniently building stencils. // /// Convenience wrapper for Stencil::cat that can be imported with a using decl. template Stencil cat(Ts &&... Parts) { return Stencil::cat(std::forward(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 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_