1 //===--- Transformer.cpp - Transformer library implementation ---*- C++ -*-===//
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
7 //===----------------------------------------------------------------------===//
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"
23 using namespace clang;
24 using namespace transformer;
26 using ast_matchers::MatchFinder;
27 using ast_matchers::internal::DynTypedMatcher;
28 using ast_type_traits::ASTNodeKind;
30 using MatchResult = MatchFinder::MatchResult;
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);
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.
45 return SmallVector<Transformation, 0>();
46 auto Replacement = Edit.Replacement(Result);
48 return Replacement.takeError();
49 transformer::detail::Transformation T;
51 T.Replacement = std::move(*Replacement);
52 Transformations.push_back(std::move(T));
54 return Transformations;
57 ASTEdit transformer::change(RangeSelector S, TextGenerator Replacement) {
59 E.TargetRange = std::move(S);
60 E.Replacement = std::move(Replacement);
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), {}}}};
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);
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>();
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(
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));
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
106 RewriteRule transformer::applyFirst(ArrayRef<RewriteRule> Rules) {
108 for (auto &Rule : Rules)
109 R.Cases.append(Rule.Cases.begin(), Rule.Cases.end());
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>>
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]);
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));
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.");
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()),
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());
161 // Finds the case that was "selected" -- that is, whose matcher triggered the
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];
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];
175 llvm_unreachable("No tag found for this rule.");
178 constexpr llvm::StringLiteral RewriteRule::RootID;