clang  11.0.0git
RewriteRule.cpp
Go to the documentation of this file.
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 
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 
23 using namespace clang;
24 using namespace transformer;
25 
27 using ast_matchers::internal::DynTypedMatcher;
28 
30 
32 translateEdits(const MatchResult &Result, ArrayRef<ASTEdit> ASTEdits) {
34  for (const auto &E : ASTEdits) {
35  Expected<CharSourceRange> Range = E.TargetRange(Result);
36  if (!Range)
37  return Range.takeError();
39  tooling::getRangeForEdit(*Range, *Result.Context);
40  // FIXME: let user specify whether to treat this case as an error or ignore
41  // it as is currently done.
42  if (!EditRange)
43  return SmallVector<Edit, 0>();
44  auto Replacement = E.Replacement->eval(Result);
45  if (!Replacement)
46  return Replacement.takeError();
48  T.Range = *EditRange;
49  T.Replacement = std::move(*Replacement);
50  T.Metadata = E.Metadata;
51  Edits.push_back(std::move(T));
52  }
53  return Edits;
54 }
55 
57  return [Edits = std::move(Edits)](const MatchResult &Result) {
58  return translateEdits(Result, Edits);
59  };
60 }
61 
63  return [Edit = std::move(Edit)](const MatchResult &Result) {
64  return translateEdits(Result, {Edit});
65  };
66 }
67 
69  ASTEdit E;
70  E.TargetRange = std::move(Target);
71  E.Replacement = std::move(Replacement);
72  return E;
73 }
74 
75 namespace {
76 /// A \c TextGenerator that always returns a fixed string.
77 class SimpleTextGenerator : public MatchComputation<std::string> {
78  std::string S;
79 
80 public:
81  SimpleTextGenerator(std::string S) : S(std::move(S)) {}
83  std::string *Result) const override {
84  Result->append(S);
85  return llvm::Error::success();
86  }
87  std::string toString() const override {
88  return (llvm::Twine("text(\"") + S + "\")").str();
89  }
90 };
91 } // namespace
92 
94  return change(std::move(S), std::make_shared<SimpleTextGenerator>(""));
95 }
96 
97 RewriteRule transformer::makeRule(ast_matchers::internal::DynTypedMatcher M,
98  EditGenerator Edits,
99  TextGenerator Explanation) {
101  std::move(M), std::move(Edits), std::move(Explanation), {}}}};
102 }
103 
104 void transformer::addInclude(RewriteRule &Rule, StringRef Header,
105  IncludeFormat Format) {
106  for (auto &Case : Rule.Cases)
107  Case.AddedIncludes.emplace_back(Header.str(), Format);
108 }
109 
110 #ifndef NDEBUG
111 // Filters for supported matcher kinds. FIXME: Explicitly list the allowed kinds
112 // (all node matcher types except for `QualType` and `Type`), rather than just
113 // banning `QualType` and `Type`.
114 static bool hasValidKind(const DynTypedMatcher &M) {
115  return !M.canConvertTo<QualType>();
116 }
117 #endif
118 
119 // Binds each rule's matcher to a unique (and deterministic) tag based on
120 // `TagBase` and the id paired with the case. All of the returned matchers have
121 // their traversal kind explicitly set, either based on a pre-set kind or to the
122 // provided `DefaultTraversalKind`.
123 static std::vector<DynTypedMatcher> taggedMatchers(
124  StringRef TagBase,
125  const SmallVectorImpl<std::pair<size_t, RewriteRule::Case>> &Cases,
126  ast_type_traits::TraversalKind DefaultTraversalKind) {
127  std::vector<DynTypedMatcher> Matchers;
128  Matchers.reserve(Cases.size());
129  for (const auto &Case : Cases) {
130  std::string Tag = (TagBase + Twine(Case.first)).str();
131  // HACK: Many matchers are not bindable, so ensure that tryBind will work.
132  DynTypedMatcher BoundMatcher(Case.second.Matcher);
133  BoundMatcher.setAllowBind(true);
134  auto M = *BoundMatcher.tryBind(Tag);
135  Matchers.push_back(!M.getTraversalKind()
136  ? M.withTraversalKind(DefaultTraversalKind)
137  : std::move(M));
138  }
139  return Matchers;
140 }
141 
142 // Simply gathers the contents of the various rules into a single rule. The
143 // actual work to combine these into an ordered choice is deferred to matcher
144 // registration.
146  RewriteRule R;
147  for (auto &Rule : Rules)
148  R.Cases.append(Rule.Cases.begin(), Rule.Cases.end());
149  return R;
150 }
151 
152 std::vector<DynTypedMatcher>
154  // Map the cases into buckets of matchers -- one for each "root" AST kind,
155  // which guarantees that they can be combined in a single anyOf matcher. Each
156  // case is paired with an identifying number that is converted to a string id
157  // in `taggedMatchers`.
158  std::map<ASTNodeKind, SmallVector<std::pair<size_t, RewriteRule::Case>, 1>>
159  Buckets;
160  const SmallVectorImpl<RewriteRule::Case> &Cases = Rule.Cases;
161  for (int I = 0, N = Cases.size(); I < N; ++I) {
162  assert(hasValidKind(Cases[I].Matcher) &&
163  "Matcher must be non-(Qual)Type node matcher");
164  Buckets[Cases[I].Matcher.getSupportedKind()].emplace_back(I, Cases[I]);
165  }
166 
167  // Each anyOf explicitly controls the traversal kind. The anyOf itself is set
168  // to `TK_AsIs` to ensure no nodes are skipped, thereby deferring to the kind
169  // of the branches. Then, each branch is either left as is, if the kind is
170  // already set, or explicitly set to `TK_IgnoreUnlessSpelledInSource`. We
171  // choose this setting, because we think it is the one most friendly to
172  // beginners, who are (largely) the target audience of Transformer.
173  std::vector<DynTypedMatcher> Matchers;
174  for (const auto &Bucket : Buckets) {
175  DynTypedMatcher M = DynTypedMatcher::constructVariadic(
176  DynTypedMatcher::VO_AnyOf, Bucket.first,
177  taggedMatchers("Tag", Bucket.second, TK_IgnoreUnlessSpelledInSource));
178  M.setAllowBind(true);
179  // `tryBind` is guaranteed to succeed, because `AllowBind` was set to true.
180  Matchers.push_back(
181  M.tryBind(RewriteRule::RootID)->withTraversalKind(TK_AsIs));
182  }
183  return Matchers;
184 }
185 
186 DynTypedMatcher transformer::detail::buildMatcher(const RewriteRule &Rule) {
187  std::vector<DynTypedMatcher> Ms = buildMatchers(Rule);
188  assert(Ms.size() == 1 && "Cases must have compatible matchers.");
189  return Ms[0];
190 }
191 
193  auto &NodesMap = Result.Nodes.getMap();
194  auto Root = NodesMap.find(RewriteRule::RootID);
195  assert(Root != NodesMap.end() && "Transformation failed: missing root node.");
197  CharSourceRange::getTokenRange(Root->second.getSourceRange()),
198  *Result.Context);
199  if (RootRange)
200  return RootRange->getBegin();
201  // The match doesn't have a coherent range, so fall back to the expansion
202  // location as the "beginning" of the match.
203  return Result.SourceManager->getExpansionLoc(
204  Root->second.getSourceRange().getBegin());
205 }
206 
207 // Finds the case that was "selected" -- that is, whose matcher triggered the
208 // `MatchResult`.
209 const RewriteRule::Case &
211  const RewriteRule &Rule) {
212  if (Rule.Cases.size() == 1)
213  return Rule.Cases[0];
214 
215  auto &NodesMap = Result.Nodes.getMap();
216  for (size_t i = 0, N = Rule.Cases.size(); i < N; ++i) {
217  std::string Tag = ("Tag" + Twine(i)).str();
218  if (NodesMap.find(Tag) != NodesMap.end())
219  return Rule.Cases[i];
220  }
221  llvm_unreachable("No tag found for this rule.");
222 }
223 
224 constexpr llvm::StringLiteral RewriteRule::RootID;
225 
226 TextGenerator tooling::text(std::string M) {
227  return std::make_shared<SimpleTextGenerator>(std::move(M));
228 }
A class to allow finding matches over the Clang AST.
A (possibly-)qualified type.
Definition: Type.h:655
void addInclude(RewriteRule &Rule, llvm::StringRef Header, IncludeFormat Format=IncludeFormat::Quoted)
For every case in Rule, adds an include directive for the given header.
static CharSourceRange getTokenRange(SourceRange R)
ast_matchers::internal::DynTypedMatcher buildMatcher(const RewriteRule &Rule)
Builds a single matcher for the rule, covering all of the rule&#39;s cases.
TraversalKind
Defines how we descend a level in the AST when we pass through expressions.
Definition: ASTTypeTraits.h:39
Description of a source-code transformation.
Definition: RewriteRule.h:135
MatchFinder::MatchResult MatchResult
Will traverse all child nodes.
Definition: ASTTypeTraits.h:41
IncludeFormat
Format of the path in an include directive – angle brackets or quotes.
Definition: RewriteRule.h:107
MatchFinder::MatchResult MatchResult
Definition: RewriteRule.cpp:29
MatchConsumer< llvm::SmallVector< Edit, 1 > > EditGenerator
Maps a match result to a list of concrete edits (with possible failure).
Definition: RewriteRule.h:46
EditGenerator editList(llvm::SmallVector< ASTEdit, 1 > Edits)
Lifts a list of ASTEdits into an EditGenerator.
Definition: RewriteRule.cpp:56
static constexpr llvm::StringLiteral RootID
Definition: RewriteRule.h:150
static Expected< SmallVector< transformer::Edit, 1 > > translateEdits(const MatchResult &Result, ArrayRef< ASTEdit > ASTEdits)
Definition: RewriteRule.cpp:32
SmallVector< Case, 1 > Cases
Definition: RewriteRule.h:146
static std::vector< DynTypedMatcher > taggedMatchers(StringRef TagBase, const SmallVectorImpl< std::pair< size_t, RewriteRule::Case >> &Cases, ast_type_traits::TraversalKind DefaultTraversalKind)
A concrete description of a source edit, represented by a character range in the source to be replace...
Definition: RewriteRule.h:36
CharSourceRange Range
Definition: RewriteRule.h:37
const RewriteRule::Case & findSelectedCase(const ast_matchers::MatchFinder::MatchResult &Result, const RewriteRule &Rule)
Returns the Case of Rule that was selected in the match result.
Contains all information for a given match.
ASTEdit remove(RangeSelector S)
Removes the source selected by S.
Definition: RewriteRule.cpp:93
std::vector< ast_matchers::internal::DynTypedMatcher > buildMatchers(const RewriteRule &Rule)
Builds a set of matchers that cover the rule.
EditGenerator edit(ASTEdit)
Definition: RewriteRule.cpp:62
MatchConsumer< CharSourceRange > RangeSelector
Definition: RangeSelector.h:27
ASTEdit change(RangeSelector Target, TextGenerator Replacement)
DEPRECATED: use changeTo.
Definition: RewriteRule.h:230
Encodes a location in the source.
clang::ASTContext *const Context
Utilities for interpreting the matched AST structures.
RewriteRule makeRule(ast_matchers::internal::DynTypedMatcher M, EditGenerator Edits, TextGenerator Explanation=nullptr)
Convenience function for constructing a simple RewriteRule.
Definition: RewriteRule.cpp:97
RewriteRule applyFirst(ArrayRef< RewriteRule > Rules)
Applies the first rule whose pattern matches; other rules are ignored.
Dataflow Directional Tag Classes.
llvm::Optional< CharSourceRange > getRangeForEdit(const CharSourceRange &EditRange, const SourceManager &SM, const LangOptions &LangOpts)
Attempts to resolve the given range to one that can be edited by a rewrite; generally, one that starts and ends within a particular file.
Definition: SourceCode.cpp:82
SourceLocation getRuleMatchLoc(const ast_matchers::MatchFinder::MatchResult &Result)
Gets the beginning location of the source matched by a rewrite rule.
std::string toString(const til::SExpr *E)
transformer::TextGenerator TextGenerator
Wraps a string as a TextGenerator.
Definition: RewriteRule.h:312
Ignore AST nodes not written in the source.
Definition: ASTTypeTraits.h:48
std::shared_ptr< MatchComputation< std::string > > TextGenerator
Definition: RewriteRule.h:48
Defines the clang::SourceLocation class and associated facilities.
ASTEdit changeTo(RangeSelector Target, TextGenerator Replacement)
Replaces a portion of the source text with Replacement.
Definition: RewriteRule.cpp:68
A failable computation over nodes bound by AST matchers, with (limited) reflection via the toString m...
Definition: MatchConsumer.h:64
static bool hasValidKind(const DynTypedMatcher &M)
TextGenerator text(std::string M)
Defines the RewriteRule class and related functions for creating, modifying and interpreting RewriteR...