]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - lib/Tooling/Transformer/RewriteRule.cpp
Vendor import of stripped clang trunk r375505, the last commit before
[FreeBSD/FreeBSD.git] / lib / Tooling / Transformer / RewriteRule.cpp
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
23 using namespace clang;
24 using namespace transformer;
25
26 using ast_matchers::MatchFinder;
27 using ast_matchers::internal::DynTypedMatcher;
28 using ast_type_traits::ASTNodeKind;
29
30 using MatchResult = MatchFinder::MatchResult;
31
32 Expected<SmallVector<transformer::detail::Transformation, 1>>
33 transformer::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
57 ASTEdit 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
64 RewriteRule 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
70 void 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`.
80 static 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.
87 static 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.
106 RewriteRule 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
113 std::vector<DynTypedMatcher>
114 transformer::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
140 DynTypedMatcher 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
146 SourceLocation 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`.
163 const RewriteRule::Case &
164 transformer::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
178 constexpr llvm::StringLiteral RewriteRule::RootID;