clang  10.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;
29 
31 
36  for (const auto &Edit : Edits) {
37  Expected<CharSourceRange> Range = Edit.TargetRange(Result);
38  if (!Range)
39  return Range.takeError();
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)
46  auto Replacement = Edit.Replacement->eval(Result);
47  if (!Replacement)
48  return Replacement.takeError();
50  T.Range = *EditRange;
51  T.Replacement = std::move(*Replacement);
52  Transformations.push_back(std::move(T));
53  }
54  return Transformations;
55 }
56 
58  ASTEdit E;
59  E.TargetRange = std::move(S);
60  E.Replacement = std::move(Replacement);
61  return E;
62 }
63 
64 namespace {
65 /// A \c TextGenerator that always returns a fixed string.
66 class SimpleTextGenerator : public MatchComputation<std::string> {
67  std::string S;
68 
69 public:
70  SimpleTextGenerator(std::string S) : S(std::move(S)) {}
72  std::string *Result) const override {
73  Result->append(S);
74  return llvm::Error::success();
75  }
76  std::string toString() const override {
77  return (llvm::Twine("text(\"") + S + "\")").str();
78  }
79 };
80 } // namespace
81 
83  return change(std::move(S), std::make_shared<SimpleTextGenerator>(""));
84 }
85 
87  TextGenerator Explanation) {
89  std::move(M), std::move(Edits), std::move(Explanation), {}}}};
90 }
91 
92 void transformer::addInclude(RewriteRule &Rule, StringRef Header,
93  IncludeFormat Format) {
94  for (auto &Case : Rule.Cases)
95  Case.AddedIncludes.emplace_back(Header.str(), Format);
96 }
97 
98 #ifndef NDEBUG
99 // Filters for supported matcher kinds. FIXME: Explicitly list the allowed kinds
100 // (all node matcher types except for `QualType` and `Type`), rather than just
101 // banning `QualType` and `Type`.
102 static bool hasValidKind(const DynTypedMatcher &M) {
103  return !M.canConvertTo<QualType>();
104 }
105 #endif
106 
107 // Binds each rule's matcher to a unique (and deterministic) tag based on
108 // `TagBase` and the id paired with the case.
109 static std::vector<DynTypedMatcher> taggedMatchers(
110  StringRef TagBase,
111  const SmallVectorImpl<std::pair<size_t, RewriteRule::Case>> &Cases) {
112  std::vector<DynTypedMatcher> Matchers;
113  Matchers.reserve(Cases.size());
114  for (const auto &Case : Cases) {
115  std::string Tag = (TagBase + Twine(Case.first)).str();
116  // HACK: Many matchers are not bindable, so ensure that tryBind will work.
117  DynTypedMatcher BoundMatcher(Case.second.Matcher);
118  BoundMatcher.setAllowBind(true);
119  auto M = BoundMatcher.tryBind(Tag);
120  Matchers.push_back(*std::move(M));
121  }
122  return Matchers;
123 }
124 
125 // Simply gathers the contents of the various rules into a single rule. The
126 // actual work to combine these into an ordered choice is deferred to matcher
127 // registration.
129  RewriteRule R;
130  for (auto &Rule : Rules)
131  R.Cases.append(Rule.Cases.begin(), Rule.Cases.end());
132  return R;
133 }
134 
135 std::vector<DynTypedMatcher>
137  // Map the cases into buckets of matchers -- one for each "root" AST kind,
138  // which guarantees that they can be combined in a single anyOf matcher. Each
139  // case is paired with an identifying number that is converted to a string id
140  // in `taggedMatchers`.
141  std::map<ASTNodeKind, SmallVector<std::pair<size_t, RewriteRule::Case>, 1>>
142  Buckets;
143  const SmallVectorImpl<RewriteRule::Case> &Cases = Rule.Cases;
144  for (int I = 0, N = Cases.size(); I < N; ++I) {
145  assert(hasValidKind(Cases[I].Matcher) &&
146  "Matcher must be non-(Qual)Type node matcher");
147  Buckets[Cases[I].Matcher.getSupportedKind()].emplace_back(I, Cases[I]);
148  }
149 
150  std::vector<DynTypedMatcher> Matchers;
151  for (const auto &Bucket : Buckets) {
152  DynTypedMatcher M = DynTypedMatcher::constructVariadic(
153  DynTypedMatcher::VO_AnyOf, Bucket.first,
154  taggedMatchers("Tag", Bucket.second));
155  M.setAllowBind(true);
156  // `tryBind` is guaranteed to succeed, because `AllowBind` was set to true.
157  Matchers.push_back(*M.tryBind(RewriteRule::RootID));
158  }
159  return Matchers;
160 }
161 
162 DynTypedMatcher transformer::detail::buildMatcher(const RewriteRule &Rule) {
163  std::vector<DynTypedMatcher> Ms = buildMatchers(Rule);
164  assert(Ms.size() == 1 && "Cases must have compatible matchers.");
165  return Ms[0];
166 }
167 
169  auto &NodesMap = Result.Nodes.getMap();
170  auto Root = NodesMap.find(RewriteRule::RootID);
171  assert(Root != NodesMap.end() && "Transformation failed: missing root node.");
173  CharSourceRange::getTokenRange(Root->second.getSourceRange()),
174  *Result.Context);
175  if (RootRange)
176  return RootRange->getBegin();
177  // The match doesn't have a coherent range, so fall back to the expansion
178  // location as the "beginning" of the match.
179  return Result.SourceManager->getExpansionLoc(
180  Root->second.getSourceRange().getBegin());
181 }
182 
183 // Finds the case that was "selected" -- that is, whose matcher triggered the
184 // `MatchResult`.
185 const RewriteRule::Case &
187  const RewriteRule &Rule) {
188  if (Rule.Cases.size() == 1)
189  return Rule.Cases[0];
190 
191  auto &NodesMap = Result.Nodes.getMap();
192  for (size_t i = 0, N = Rule.Cases.size(); i < N; ++i) {
193  std::string Tag = ("Tag" + Twine(i)).str();
194  if (NodesMap.find(Tag) != NodesMap.end())
195  return Rule.Cases[i];
196  }
197  llvm_unreachable("No tag found for this rule.");
198 }
199 
200 constexpr llvm::StringLiteral RewriteRule::RootID;
201 
202 TextGenerator tooling::text(std::string M) {
203  return std::make_shared<SimpleTextGenerator>(std::move(M));
204 }
A class to allow finding matches over the Clang AST.
A (possibly-)qualified type.
Definition: Type.h:654
Expected< SmallVector< Transformation, 1 > > translateEdits(const ast_matchers::MatchFinder::MatchResult &Result, llvm::ArrayRef< ASTEdit > Edits)
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.
Definition: RewriteRule.cpp:33
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.
Description of a source-code transformation.
Definition: RewriteRule.h:106
MatchFinder::MatchResult MatchResult
IncludeFormat
Format of the path in an include directive – angle brackets or quotes.
Definition: RewriteRule.h:78
MatchFinder::MatchResult MatchResult
Definition: RewriteRule.cpp:30
static std::string toString(const clang::SanitizerSet &Sanitizers)
Produce a string containing comma-separated names of sanitizers in Sanitizers set.
static constexpr llvm::StringLiteral RootID
Definition: RewriteRule.h:121
SmallVector< Case, 1 > Cases
Definition: RewriteRule.h:117
static std::vector< DynTypedMatcher > taggedMatchers(StringRef TagBase, const SmallVectorImpl< std::pair< size_t, RewriteRule::Case >> &Cases)
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:82
std::vector< ast_matchers::internal::DynTypedMatcher > buildMatchers(const RewriteRule &Rule)
Builds a set of matchers that cover the rule (one for each distinct node matcher base kind: Stmt...
MatchConsumer< CharSourceRange > RangeSelector
Definition: RangeSelector.h:27
ASTEdit change(RangeSelector Target, TextGenerator Replacement)
DEPRECATED: use changeTo.
Definition: RewriteRule.h:195
Encodes a location in the source.
RewriteRule applyFirst(ArrayRef< RewriteRule > Rules)
Applies the first rule whose pattern matches; other rules are ignored.
RewriteRule makeRule(ast_matchers::internal::DynTypedMatcher M, SmallVector< ASTEdit, 1 > Edits, TextGenerator Explanation=nullptr)
Convenience function for constructing a simple RewriteRule.
Dataflow Directional Tag Classes.
llvm::Optional< CharSourceRange > getRangeForEdit(const CharSourceRange &EditRange, const SourceManager &SM, const LangOptions &LangOpts)
Definition: SourceCode.cpp:34
A source "transformation," represented by a character range in the source to be replaced and a corres...
Definition: RewriteRule.h:265
SourceLocation getRuleMatchLoc(const ast_matchers::MatchFinder::MatchResult &Result)
Gets the beginning location of the source matched by a rewrite rule.
transformer::TextGenerator TextGenerator
Wraps a string as a TextGenerator.
Definition: RewriteRule.h:291
std::shared_ptr< MatchComputation< std::string > > TextGenerator
Definition: RewriteRule.h:33
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:57
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...