reference, declarationdefinition
definition → references, declarations, derived classes, virtual overrides
reference to multiple definitions → definitions
unreferenced
    1
    2
    3
    4
    5
    6
    7
    8
    9
   10
   11
   12
   13
   14
   15
   16
   17
   18
   19
   20
   21
   22
   23
   24
   25
   26
   27
   28
   29
   30
   31
   32
   33
   34
   35
   36
   37
   38
   39
   40
   41
   42
   43
   44
   45
   46
   47
   48
   49
   50
   51
   52
   53
   54
   55
   56
   57
   58
   59
   60
   61
   62
   63
   64
   65
   66
   67
   68
   69
   70
   71
   72
   73
   74
   75
   76
   77
   78
   79
   80
   81
   82
   83
   84
   85
   86
   87
   88
   89
   90
   91
   92
   93
   94
   95
   96
   97
   98
   99
  100
  101
  102
  103
  104
  105
  106
  107
  108
  109
  110
  111
  112
  113
  114
  115
  116
  117
  118
  119
  120
  121
  122
  123
  124
  125
  126
  127
  128
  129
  130
  131
  132
  133
  134
  135
  136
  137
  138
  139
  140
  141
  142
  143
  144
  145
  146
  147
  148
  149
  150
  151
  152
  153
  154
  155
  156
  157
  158
  159
  160
  161
  162
  163
  164
  165
  166
  167
  168
  169
  170
  171
  172
  173
  174
  175
  176
  177
  178
  179
  180
  181
  182
  183
  184
  185
  186
  187
  188
  189
  190
  191
  192
  193
  194
  195
  196
  197
  198
  199
  200
  201
  202
  203
  204
  205
  206
  207
  208
  209
  210
  211
  212
  213
  214
  215
  216
  217
  218
  219
  220
  221
  222
  223
  224
  225
  226
  227
  228
  229
  230
  231
  232
  233
  234
  235
  236
  237
  238
  239
  240
  241
  242
  243
  244
  245
  246
  247
  248
  249
  250
  251
  252
  253
  254
  255
  256
  257
  258
  259
  260
  261
  262
  263
  264
  265
  266
  267
  268
  269
  270
  271
  272
  273
  274
  275
  276
  277
  278
  279
  280
  281
  282
  283
  284
  285
  286
  287
  288
  289
  290
  291
  292
  293
  294
  295
  296
  297
  298
  299
  300
  301
  302
  303
  304
  305
  306
  307
  308
  309
//===--- RewriteRule.h - RewriteRule 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
///  Defines the RewriteRule class and related functions for creating,
///  modifying and interpreting RewriteRules.
///
//===----------------------------------------------------------------------===//

#ifndef LLVM_CLANG_TOOLING_TRANSFORMER_REWRITE_RULE_H_
#define LLVM_CLANG_TOOLING_TRANSFORMER_REWRITE_RULE_H_

#include "clang/ASTMatchers/ASTMatchFinder.h"
#include "clang/ASTMatchers/ASTMatchers.h"
#include "clang/ASTMatchers/ASTMatchersInternal.h"
#include "clang/Tooling/Refactoring/AtomicChange.h"
#include "clang/Tooling/Transformer/MatchConsumer.h"
#include "clang/Tooling/Transformer/RangeSelector.h"
#include "llvm/ADT/STLExtras.h"
#include "llvm/ADT/SmallVector.h"
#include "llvm/Support/Error.h"
#include <functional>
#include <string>
#include <utility>

namespace clang {
namespace transformer {
using TextGenerator = MatchConsumer<std::string>;

// 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 functions. For
// example,
// \code
//   change(name(fun), text("Frodo"))
// \endcode
// Or, if we use Stencil for the TextGenerator:
// \code
//   using stencil::cat;
//   change(statement(thenNode), cat("{", thenNode, "}"))
//   change(callArgs(call), cat(x, ",", y))
// \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(cat("different_expr"))
// \endcode
struct ASTEdit {
  RangeSelector TargetRange;
  TextGenerator Replacement;
  TextGenerator Note;
};

/// Format of the path in an include directive -- angle brackets or quotes.
enum class IncludeFormat {
  Quoted,
  Angled,
};

/// Description of a source-code transformation.
//
// A *rewrite rule* describes a transformation of source code. A simple rule
// contains each of 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.
//
// However, rules can also consist of (sub)rules, where the first that matches
// is applied and the rest are ignored.  So, the above components are gathered
// as a `Case` and a rule is a list of cases.
//
// Rule cases have an additional, implicit, component: the parameters. These are
// portions of the pattern which are left unspecified, yet bound in the pattern
// so that we can reference them in the edits.
//
// The \c Transformer class can be used to apply the rewrite rule and obtain the
// corresponding replacements.
struct RewriteRule {
  struct Case {
    ast_matchers::internal::DynTypedMatcher Matcher;
    SmallVector<ASTEdit, 1> Edits;
    TextGenerator Explanation;
    // Include paths to add to the file affected by this case.  These are
    // bundled with the `Case`, rather than the `RewriteRule`, because each case
    // might have different associated changes to the includes.
    std::vector<std::pair<std::string, IncludeFormat>> AddedIncludes;
  };
  // We expect RewriteRules will most commonly include only one case.
  SmallVector<Case, 1> Cases;

  // 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 simple \c RewriteRule.
RewriteRule makeRule(ast_matchers::internal::DynTypedMatcher M,
                     SmallVector<ASTEdit, 1> Edits,
                     TextGenerator Explanation = nullptr);

/// Convenience overload of \c makeRule for common case of only one edit.
inline RewriteRule makeRule(ast_matchers::internal::DynTypedMatcher M,
                            ASTEdit Edit,
                            TextGenerator Explanation = nullptr) {
  SmallVector<ASTEdit, 1> Edits;
  Edits.emplace_back(std::move(Edit));
  return makeRule(std::move(M), std::move(Edits), std::move(Explanation));
}

/// For every case in Rule, adds an include directive for the given header. The
/// common use is assumed to be a rule with only one case. For example, to
/// replace a function call and add headers corresponding to the new code, one
/// could write:
/// \code
///   auto R = makeRule(callExpr(callee(functionDecl(hasName("foo")))),
///            change(text("bar()")));
///   AddInclude(R, "path/to/bar_header.h");
///   AddInclude(R, "vector", IncludeFormat::Angled);
/// \endcode
void addInclude(RewriteRule &Rule, llvm::StringRef Header,
                IncludeFormat Format = IncludeFormat::Quoted);

/// Applies the first rule whose pattern matches; other rules are ignored.  If
/// the matchers are independent then order doesn't matter. In that case,
/// `applyFirst` is simply joining the set of rules into one.
//
// `applyFirst` is like an `anyOf` matcher with an edit action attached to each
// of its cases. Anywhere you'd use `anyOf(m1.bind("id1"), m2.bind("id2"))` and
// then dispatch on those ids in your code for control flow, `applyFirst` lifts
// that behavior to the rule level.  So, you can write `applyFirst({makeRule(m1,
// action1), makeRule(m2, action2), ...});`
//
// For example, consider a type `T` with a deterministic serialization function,
// `serialize()`.  For performance reasons, we would like to make it
// non-deterministic.  Therefore, we want to drop the expectation that
// `a.serialize() = b.serialize() iff a = b` (although we'll maintain
// `deserialize(a.serialize()) = a`).
//
// We have three cases to consider (for some equality function, `eq`):
// ```
// eq(a.serialize(), b.serialize()) --> eq(a,b)
// eq(a, b.serialize())             --> eq(deserialize(a), b)
// eq(a.serialize(), b)             --> eq(a, deserialize(b))
// ```
//
// `applyFirst` allows us to specify each independently:
// ```
// auto eq_fun = functionDecl(...);
// auto method_call = cxxMemberCallExpr(...);
//
// auto two_calls = callExpr(callee(eq_fun), hasArgument(0, method_call),
//                           hasArgument(1, method_call));
// auto left_call =
//     callExpr(callee(eq_fun), callExpr(hasArgument(0, method_call)));
// auto right_call =
//     callExpr(callee(eq_fun), callExpr(hasArgument(1, method_call)));
//
// RewriteRule R = applyFirst({makeRule(two_calls, two_calls_action),
//                             makeRule(left_call, left_call_action),
//                             makeRule(right_call, right_call_action)});
// ```
RewriteRule applyFirst(ArrayRef<RewriteRule> Rules);

/// Replaces a portion of the source text with \p Replacement.
ASTEdit change(RangeSelector Target, TextGenerator Replacement);

/// Replaces the entirety of a RewriteRule's match with \p Replacement.  For
/// example, to replace a function call, one could write:
/// \code
///   makeRule(callExpr(callee(functionDecl(hasName("foo")))),
///            change(text("bar()")))
/// \endcode
inline ASTEdit change(TextGenerator Replacement) {
  return change(node(RewriteRule::RootID), std::move(Replacement));
}

/// Inserts \p Replacement before \p S, leaving the source selected by \S
/// unchanged.
inline ASTEdit insertBefore(RangeSelector S, TextGenerator Replacement) {
  return change(before(std::move(S)), std::move(Replacement));
}

/// Inserts \p Replacement after \p S, leaving the source selected by \S
/// unchanged.
inline ASTEdit insertAfter(RangeSelector S, TextGenerator Replacement) {
  return change(after(std::move(S)), std::move(Replacement));
}

/// Removes the source selected by \p S.
inline ASTEdit remove(RangeSelector S) {
  return change(std::move(S),
                [](const ast_matchers::MatchFinder::MatchResult &)
                    -> Expected<std::string> { return ""; });
}

/// The following three functions are a low-level part of the RewriteRule
/// API. We expose them for use in implementing the fixtures that interpret
/// RewriteRule, like Transformer and TransfomerTidy, or for more advanced
/// users.
//
// FIXME: These functions are really public, if advanced, elements of the
// RewriteRule API.  Recast them as such.  Or, just declare these functions
// public and well-supported and move them out of `detail`.
namespace detail {
/// Builds a single matcher for the rule, covering all of the rule's cases.
/// Only supports Rules whose cases' matchers share the same base "kind"
/// (`Stmt`, `Decl`, etc.)  Deprecated: use `buildMatchers` instead, which
/// supports mixing matchers of different kinds.
ast_matchers::internal::DynTypedMatcher buildMatcher(const RewriteRule &Rule);

/// Builds a set of matchers that cover the rule (one for each distinct node
/// matcher base kind: Stmt, Decl, etc.). Node-matchers for `QualType` and
/// `Type` are not permitted, since such nodes carry no source location
/// information and are therefore not relevant for rewriting. If any such
/// matchers are included, will return an empty vector.
std::vector<ast_matchers::internal::DynTypedMatcher>
buildMatchers(const RewriteRule &Rule);

/// Gets the beginning location of the source matched by a rewrite rule. If the
/// match occurs within a macro expansion, returns the beginning of the
/// expansion point. `Result` must come from the matching of a rewrite rule.
SourceLocation
getRuleMatchLoc(const ast_matchers::MatchFinder::MatchResult &Result);

/// Returns the \c Case of \c Rule that was selected in the match result.
/// Assumes a matcher built with \c buildMatcher.
const RewriteRule::Case &
findSelectedCase(const ast_matchers::MatchFinder::MatchResult &Result,
                 const RewriteRule &Rule);

/// 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.
///
/// 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);
} // namespace detail
} // namespace transformer

namespace tooling {
// DEPRECATED: These are temporary aliases supporting client migration to the
// `transformer` namespace.
/// Wraps a string as a TextGenerator.
using TextGenerator = transformer::TextGenerator;

inline TextGenerator text(std::string M) {
  return [M](const ast_matchers::MatchFinder::MatchResult &)
             -> Expected<std::string> { return M; };
}

using transformer::addInclude;
using transformer::applyFirst;
using transformer::change;
using transformer::insertAfter;
using transformer::insertBefore;
using transformer::makeRule;
using transformer::remove;
using transformer::RewriteRule;
using transformer::IncludeFormat;
namespace detail {
using namespace transformer::detail;
} // namespace detail
} // namespace tooling
} // namespace clang

#endif // LLVM_CLANG_TOOLING_TRANSFORMER_REWRITE_RULE_H_