RewriteRule.cpp revision 1.1.1.1
1//===--- Transformer.cpp - Transformer library implementation ---*- C++ -*-===//
2//
3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4// See https://llvm.org/LICENSE.txt for license information.
5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6//
7//===----------------------------------------------------------------------===//
8
9#include "clang/Tooling/Transformer/RewriteRule.h"
10#include "clang/ASTMatchers/ASTMatchFinder.h"
11#include "clang/ASTMatchers/ASTMatchers.h"
12#include "clang/Basic/SourceLocation.h"
13#include "clang/Tooling/Transformer/SourceCode.h"
14#include "llvm/ADT/Optional.h"
15#include "llvm/ADT/StringRef.h"
16#include "llvm/Support/Errc.h"
17#include "llvm/Support/Error.h"
18#include <map>
19#include <string>
20#include <utility>
21#include <vector>
22
23using namespace clang;
24using namespace transformer;
25
26using ast_matchers::MatchFinder;
27using ast_matchers::internal::DynTypedMatcher;
28using ast_type_traits::ASTNodeKind;
29
30using MatchResult = MatchFinder::MatchResult;
31
32Expected<SmallVector<transformer::detail::Transformation, 1>>
33transformer::detail::translateEdits(const MatchResult &Result,
34                                llvm::ArrayRef<ASTEdit> Edits) {
35  SmallVector<transformer::detail::Transformation, 1> Transformations;
36  for (const auto &Edit : Edits) {
37    Expected<CharSourceRange> Range = Edit.TargetRange(Result);
38    if (!Range)
39      return Range.takeError();
40    llvm::Optional<CharSourceRange> EditRange =
41        tooling::getRangeForEdit(*Range, *Result.Context);
42    // FIXME: let user specify whether to treat this case as an error or ignore
43    // it as is currently done.
44    if (!EditRange)
45      return SmallVector<Transformation, 0>();
46    auto Replacement = Edit.Replacement(Result);
47    if (!Replacement)
48      return Replacement.takeError();
49    transformer::detail::Transformation T;
50    T.Range = *EditRange;
51    T.Replacement = std::move(*Replacement);
52    Transformations.push_back(std::move(T));
53  }
54  return Transformations;
55}
56
57ASTEdit transformer::change(RangeSelector S, TextGenerator Replacement) {
58  ASTEdit E;
59  E.TargetRange = std::move(S);
60  E.Replacement = std::move(Replacement);
61  return E;
62}
63
64RewriteRule transformer::makeRule(DynTypedMatcher M, SmallVector<ASTEdit, 1> Edits,
65                              TextGenerator Explanation) {
66  return RewriteRule{{RewriteRule::Case{
67      std::move(M), std::move(Edits), std::move(Explanation), {}}}};
68}
69
70void transformer::addInclude(RewriteRule &Rule, StringRef Header,
71                         IncludeFormat Format) {
72  for (auto &Case : Rule.Cases)
73    Case.AddedIncludes.emplace_back(Header.str(), Format);
74}
75
76#ifndef NDEBUG
77// Filters for supported matcher kinds. FIXME: Explicitly list the allowed kinds
78// (all node matcher types except for `QualType` and `Type`), rather than just
79// banning `QualType` and `Type`.
80static bool hasValidKind(const DynTypedMatcher &M) {
81  return !M.canConvertTo<QualType>();
82}
83#endif
84
85// Binds each rule's matcher to a unique (and deterministic) tag based on
86// `TagBase` and the id paired with the case.
87static std::vector<DynTypedMatcher> taggedMatchers(
88    StringRef TagBase,
89    const SmallVectorImpl<std::pair<size_t, RewriteRule::Case>> &Cases) {
90  std::vector<DynTypedMatcher> Matchers;
91  Matchers.reserve(Cases.size());
92  for (const auto &Case : Cases) {
93    std::string Tag = (TagBase + Twine(Case.first)).str();
94    // HACK: Many matchers are not bindable, so ensure that tryBind will work.
95    DynTypedMatcher BoundMatcher(Case.second.Matcher);
96    BoundMatcher.setAllowBind(true);
97    auto M = BoundMatcher.tryBind(Tag);
98    Matchers.push_back(*std::move(M));
99  }
100  return Matchers;
101}
102
103// Simply gathers the contents of the various rules into a single rule. The
104// actual work to combine these into an ordered choice is deferred to matcher
105// registration.
106RewriteRule transformer::applyFirst(ArrayRef<RewriteRule> Rules) {
107  RewriteRule R;
108  for (auto &Rule : Rules)
109    R.Cases.append(Rule.Cases.begin(), Rule.Cases.end());
110  return R;
111}
112
113std::vector<DynTypedMatcher>
114transformer::detail::buildMatchers(const RewriteRule &Rule) {
115  // Map the cases into buckets of matchers -- one for each "root" AST kind,
116  // which guarantees that they can be combined in a single anyOf matcher. Each
117  // case is paired with an identifying number that is converted to a string id
118  // in `taggedMatchers`.
119  std::map<ASTNodeKind, SmallVector<std::pair<size_t, RewriteRule::Case>, 1>>
120      Buckets;
121  const SmallVectorImpl<RewriteRule::Case> &Cases = Rule.Cases;
122  for (int I = 0, N = Cases.size(); I < N; ++I) {
123    assert(hasValidKind(Cases[I].Matcher) &&
124           "Matcher must be non-(Qual)Type node matcher");
125    Buckets[Cases[I].Matcher.getSupportedKind()].emplace_back(I, Cases[I]);
126  }
127
128  std::vector<DynTypedMatcher> Matchers;
129  for (const auto &Bucket : Buckets) {
130    DynTypedMatcher M = DynTypedMatcher::constructVariadic(
131        DynTypedMatcher::VO_AnyOf, Bucket.first,
132        taggedMatchers("Tag", Bucket.second));
133    M.setAllowBind(true);
134    // `tryBind` is guaranteed to succeed, because `AllowBind` was set to true.
135    Matchers.push_back(*M.tryBind(RewriteRule::RootID));
136  }
137  return Matchers;
138}
139
140DynTypedMatcher transformer::detail::buildMatcher(const RewriteRule &Rule) {
141  std::vector<DynTypedMatcher> Ms = buildMatchers(Rule);
142  assert(Ms.size() == 1 && "Cases must have compatible matchers.");
143  return Ms[0];
144}
145
146SourceLocation transformer::detail::getRuleMatchLoc(const MatchResult &Result) {
147  auto &NodesMap = Result.Nodes.getMap();
148  auto Root = NodesMap.find(RewriteRule::RootID);
149  assert(Root != NodesMap.end() && "Transformation failed: missing root node.");
150  llvm::Optional<CharSourceRange> RootRange = tooling::getRangeForEdit(
151      CharSourceRange::getTokenRange(Root->second.getSourceRange()),
152      *Result.Context);
153  if (RootRange)
154    return RootRange->getBegin();
155  // The match doesn't have a coherent range, so fall back to the expansion
156  // location as the "beginning" of the match.
157  return Result.SourceManager->getExpansionLoc(
158      Root->second.getSourceRange().getBegin());
159}
160
161// Finds the case that was "selected" -- that is, whose matcher triggered the
162// `MatchResult`.
163const RewriteRule::Case &
164transformer::detail::findSelectedCase(const MatchResult &Result,
165                                  const RewriteRule &Rule) {
166  if (Rule.Cases.size() == 1)
167    return Rule.Cases[0];
168
169  auto &NodesMap = Result.Nodes.getMap();
170  for (size_t i = 0, N = Rule.Cases.size(); i < N; ++i) {
171    std::string Tag = ("Tag" + Twine(i)).str();
172    if (NodesMap.find(Tag) != NodesMap.end())
173      return Rule.Cases[i];
174  }
175  llvm_unreachable("No tag found for this rule.");
176}
177
178constexpr llvm::StringLiteral RewriteRule::RootID;
179