blob: feb3ac88531daa996d7ae1863e586ae7171bfb8b [file] [log] [blame]
//===--- 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 {
namespace stencil {
// DEPRECATED: These are temporary aliases supporting client migration to the
// `transformer` namespace.
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_