blob: e99e279360f41b50b0f7d7d54398bffe4d3352c5 [file] [log] [blame]
//===--- Transformer.h - Clang source-rewriting library ---------*- 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
/// Defines a library supporting the concise specification of clang-based
/// source-to-source transformations.
///
//===----------------------------------------------------------------------===//
#ifndef LLVM_CLANG_TOOLING_REFACTOR_TRANSFORMER_H_
#define LLVM_CLANG_TOOLING_REFACTOR_TRANSFORMER_H_
#include "clang/ASTMatchers/ASTMatchFinder.h"
#include "clang/ASTMatchers/ASTMatchers.h"
#include "clang/ASTMatchers/ASTMatchersInternal.h"
#include "clang/Tooling/Refactoring/AtomicChange.h"
#include "llvm/ADT/STLExtras.h"
#include "llvm/ADT/SmallVector.h"
#include "llvm/Support/Error.h"
#include <deque>
#include <functional>
#include <string>
#include <type_traits>
#include <utility>
namespace clang {
namespace tooling {
/// Determines the part of the AST node to replace. We support this to work
/// around the fact that the AST does not differentiate various syntactic
/// elements into their own nodes, so users can specify them relative to a node,
/// instead.
enum class NodePart {
/// The node itself.
Node,
/// Given a \c MemberExpr, selects the member's token.
Member,
/// Given a \c NamedDecl or \c CxxCtorInitializer, selects that token of the
/// relevant name, not including qualifiers.
Name,
};
using TextGenerator =
std::function<std::string(const ast_matchers::MatchFinder::MatchResult &)>;
/// Wraps a string as a TextGenerator.
inline TextGenerator text(std::string M) {
return [M](const ast_matchers::MatchFinder::MatchResult &) { return M; };
}
// Description of a source-code edit, expressed in terms of an AST node.
// Includes: an ID for the (bound) node, a selector for source related to the
// node, a replacement and, optionally, an explanation for the edit.
//
// * Target: the source code impacted by the rule. This identifies an AST node,
// or part thereof (\c Part), whose source range indicates the extent of the
// replacement applied by the replacement term. By default, the extent is the
// node matched by the pattern term (\c NodePart::Node). Target's are typed
// (\c Kind), which guides the determination of the node extent.
//
// * Replacement: a function that produces a replacement string for the target,
// based on the match result.
//
// * Note: (optional) a note specifically for this edit, potentially referencing
// elements of the match. This will be displayed to the user, where possible;
// for example, in clang-tidy diagnostics. Use of notes should be rare --
// explanations of the entire rewrite should be set in the rule
// (`RewriteRule::Explanation`) instead. Notes serve the rare cases wherein
// edit-specific diagnostics are required.
//
// `ASTEdit` should be built using the `change` convenience fucntions. For
// example,
// \code
// change<FunctionDecl>(fun, NodePart::Name, "Frodo")
// \endcode
// Or, if we use Stencil for the TextGenerator:
// \code
// change<Stmt>(thenNode, stencil::cat("{", thenNode, "}"))
// change<Expr>(call, NodePart::Args, stencil::cat(x, ",", y))
// .note("argument order changed.")
// \endcode
// Or, if you are changing the node corresponding to the rule's matcher, you can
// use the single-argument override of \c change:
// \code
// change<Expr>("different_expr")
// \endcode
struct ASTEdit {
// The (bound) id of the node whose source will be replaced. This id should
// never be the empty string.
std::string Target;
ast_type_traits::ASTNodeKind Kind;
NodePart Part;
TextGenerator Replacement;
TextGenerator Note;
};
// Convenience functions for creating \c ASTEdits. They all must be explicitly
// instantiated with the desired AST type. Each overload includes both \c
// std::string and \c TextGenerator versions.
// FIXME: For overloads taking a \c NodePart, constrain the valid values of \c
// Part based on the type \c T.
template <typename T>
ASTEdit change(StringRef Target, NodePart Part, TextGenerator Replacement) {
ASTEdit E;
E.Target = Target.str();
E.Kind = ast_type_traits::ASTNodeKind::getFromNodeKind<T>();
E.Part = Part;
E.Replacement = std::move(Replacement);
return E;
}
template <typename T>
ASTEdit change(StringRef Target, NodePart Part, std::string Replacement) {
return change<T>(Target, Part, text(std::move(Replacement)));
}
/// Variant of \c change for which the NodePart defaults to the whole node.
template <typename T>
ASTEdit change(StringRef Target, TextGenerator Replacement) {
return change<T>(Target, NodePart::Node, std::move(Replacement));
}
/// Variant of \c change for which the NodePart defaults to the whole node.
template <typename T>
ASTEdit change(StringRef Target, std::string Replacement) {
return change<T>(Target, text(std::move(Replacement)));
}
/// Variant of \c change that selects the node of the entire match.
template <typename T> ASTEdit change(TextGenerator Replacement);
/// Variant of \c change that selects the node of the entire match.
template <typename T> ASTEdit change(std::string Replacement) {
return change<T>(text(std::move(Replacement)));
}
/// Description of a source-code transformation.
//
// A *rewrite rule* describes a transformation of source code. It has the
// following components:
//
// * Matcher: the pattern term, expressed as clang matchers (with Transformer
// extensions).
//
// * Edits: a set of Edits to the source code, described with ASTEdits.
//
// * Explanation: explanation of the rewrite. This will be displayed to the
// user, where possible; for example, in clang-tidy diagnostics.
//
// Rules have an additional, implicit, component: the parameters. These are
// portions of the pattern which are left unspecified, yet named so that we can
// reference them in the replacement term. The structure of parameters can be
// partially or even fully specified, in which case they serve just to identify
// matched nodes for later reference rather than abstract over portions of the
// AST. However, in all cases, we refer to named portions of the pattern as
// parameters.
//
// The \c Transformer class should be used to apply the rewrite rule and obtain
// the corresponding replacements.
struct RewriteRule {
// `Matcher` describes the context of this rule. It should always be bound to
// at least `RootId`.
ast_matchers::internal::DynTypedMatcher Matcher;
SmallVector<ASTEdit, 1> Edits;
TextGenerator Explanation;
// Id used as the default target of each match. The node described by the
// matcher is should always be bound to this id.
static constexpr llvm::StringLiteral RootId = "___root___";
};
/// Convenience function for constructing a \c RewriteRule. Takes care of
/// binding the matcher to RootId.
RewriteRule makeRule(ast_matchers::internal::DynTypedMatcher M,
SmallVector<ASTEdit, 1> Edits);
/// Convenience overload of \c makeRule for common case of only one edit.
inline RewriteRule makeRule(ast_matchers::internal::DynTypedMatcher M,
ASTEdit Edit) {
SmallVector<ASTEdit, 1> Edits;
Edits.emplace_back(std::move(Edit));
return makeRule(std::move(M), std::move(Edits));
}
// Define this overload of `change` here because RewriteRule::RootId is not in
// scope at the declaration point above.
template <typename T> ASTEdit change(TextGenerator Replacement) {
return change<T>(RewriteRule::RootId, NodePart::Node, std::move(Replacement));
}
/// A source "transformation," represented by a character range in the source to
/// be replaced and a corresponding replacement string.
struct Transformation {
CharSourceRange Range;
std::string Replacement;
};
/// Attempts to translate `Edits`, which are in terms of AST nodes bound in the
/// match `Result`, into Transformations, which are in terms of the source code
/// text. This function is a low-level part of the API, provided to support
/// interpretation of a \c RewriteRule in a tool, like \c Transformer, rather
/// than direct use by end users.
///
/// Returns an empty vector if any of the edits apply to portions of the source
/// that are ineligible for rewriting (certain interactions with macros, for
/// example). Fails if any invariants are violated relating to bound nodes in
/// the match. However, it does not fail in the case of conflicting edits --
/// conflict handling is left to clients. We recommend use of the \c
/// AtomicChange or \c Replacements classes for assistance in detecting such
/// conflicts.
Expected<SmallVector<Transformation, 1>>
translateEdits(const ast_matchers::MatchFinder::MatchResult &Result,
llvm::ArrayRef<ASTEdit> Edits);
/// Handles the matcher and callback registration for a single rewrite rule, as
/// defined by the arguments of the constructor.
class Transformer : public ast_matchers::MatchFinder::MatchCallback {
public:
using ChangeConsumer =
std::function<void(const clang::tooling::AtomicChange &Change)>;
/// \param Consumer Receives each successful rewrite as an \c AtomicChange.
/// Note that clients are responsible for handling the case that independent
/// \c AtomicChanges conflict with each other.
Transformer(RewriteRule Rule, ChangeConsumer Consumer)
: Rule(std::move(Rule)), Consumer(std::move(Consumer)) {}
/// N.B. Passes `this` pointer to `MatchFinder`. So, this object should not
/// be moved after this call.
void registerMatchers(ast_matchers::MatchFinder *MatchFinder);
/// Not called directly by users -- called by the framework, via base class
/// pointer.
void run(const ast_matchers::MatchFinder::MatchResult &Result) override;
private:
RewriteRule Rule;
/// Receives each successful rewrites as an \c AtomicChange.
ChangeConsumer Consumer;
};
} // namespace tooling
} // namespace clang
#endif // LLVM_CLANG_TOOLING_REFACTOR_TRANSFORMER_H_