blob: 20d3a371950af9c59f88272846f5c72fd5f5f7a6 [file] [log] [blame]
Yitzhak Mandelbaume38c36b2019-10-11 14:43:46 +00001//===--- 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;
Yitzhak Mandelbaum8bb47cd2019-10-16 01:06:46 +000024using namespace transformer;
Yitzhak Mandelbaume38c36b2019-10-11 14:43:46 +000025
26using ast_matchers::MatchFinder;
27using ast_matchers::internal::DynTypedMatcher;
28using ast_type_traits::ASTNodeKind;
29
30using MatchResult = MatchFinder::MatchResult;
31
Yitzhak Mandelbaum8bb47cd2019-10-16 01:06:46 +000032Expected<SmallVector<transformer::detail::Transformation, 1>>
33transformer::detail::translateEdits(const MatchResult &Result,
Yitzhak Mandelbaume38c36b2019-10-11 14:43:46 +000034 llvm::ArrayRef<ASTEdit> Edits) {
Yitzhak Mandelbaum8bb47cd2019-10-16 01:06:46 +000035 SmallVector<transformer::detail::Transformation, 1> Transformations;
Yitzhak Mandelbaume38c36b2019-10-11 14:43:46 +000036 for (const auto &Edit : Edits) {
37 Expected<CharSourceRange> Range = Edit.TargetRange(Result);
38 if (!Range)
39 return Range.takeError();
40 llvm::Optional<CharSourceRange> EditRange =
Yitzhak Mandelbaum8bb47cd2019-10-16 01:06:46 +000041 tooling::getRangeForEdit(*Range, *Result.Context);
Yitzhak Mandelbaume38c36b2019-10-11 14:43:46 +000042 // 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>();
Yitzhak Mandelbaum489449c2019-11-04 08:30:18 -050046 auto Replacement = Edit.Replacement->eval(Result);
Yitzhak Mandelbaume38c36b2019-10-11 14:43:46 +000047 if (!Replacement)
48 return Replacement.takeError();
Yitzhak Mandelbaum8bb47cd2019-10-16 01:06:46 +000049 transformer::detail::Transformation T;
Yitzhak Mandelbaume38c36b2019-10-11 14:43:46 +000050 T.Range = *EditRange;
51 T.Replacement = std::move(*Replacement);
52 Transformations.push_back(std::move(T));
53 }
54 return Transformations;
55}
56
Yitzhak Mandelbaum6c683aa2019-11-06 11:30:02 -050057ASTEdit transformer::changeTo(RangeSelector S, TextGenerator Replacement) {
Yitzhak Mandelbaume38c36b2019-10-11 14:43:46 +000058 ASTEdit E;
59 E.TargetRange = std::move(S);
60 E.Replacement = std::move(Replacement);
61 return E;
62}
63
Yitzhak Mandelbaum489449c2019-11-04 08:30:18 -050064namespace {
65/// A \c TextGenerator that always returns a fixed string.
66class SimpleTextGenerator : public MatchComputation<std::string> {
67 std::string S;
68
69public:
70 SimpleTextGenerator(std::string S) : S(std::move(S)) {}
71 llvm::Error eval(const ast_matchers::MatchFinder::MatchResult &,
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
82ASTEdit transformer::remove(RangeSelector S) {
83 return change(std::move(S), std::make_shared<SimpleTextGenerator>(""));
84}
85
Yitzhak Mandelbaum8bb47cd2019-10-16 01:06:46 +000086RewriteRule transformer::makeRule(DynTypedMatcher M, SmallVector<ASTEdit, 1> Edits,
Yitzhak Mandelbaume38c36b2019-10-11 14:43:46 +000087 TextGenerator Explanation) {
88 return RewriteRule{{RewriteRule::Case{
89 std::move(M), std::move(Edits), std::move(Explanation), {}}}};
90}
91
Yitzhak Mandelbaum8bb47cd2019-10-16 01:06:46 +000092void transformer::addInclude(RewriteRule &Rule, StringRef Header,
Yitzhak Mandelbaume38c36b2019-10-11 14:43:46 +000093 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`.
102static 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.
109static 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.
Yitzhak Mandelbaum8bb47cd2019-10-16 01:06:46 +0000128RewriteRule transformer::applyFirst(ArrayRef<RewriteRule> Rules) {
Yitzhak Mandelbaume38c36b2019-10-11 14:43:46 +0000129 RewriteRule R;
130 for (auto &Rule : Rules)
131 R.Cases.append(Rule.Cases.begin(), Rule.Cases.end());
132 return R;
133}
134
135std::vector<DynTypedMatcher>
Yitzhak Mandelbaum8bb47cd2019-10-16 01:06:46 +0000136transformer::detail::buildMatchers(const RewriteRule &Rule) {
Yitzhak Mandelbaume38c36b2019-10-11 14:43:46 +0000137 // 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
Yitzhak Mandelbaum8bb47cd2019-10-16 01:06:46 +0000162DynTypedMatcher transformer::detail::buildMatcher(const RewriteRule &Rule) {
Yitzhak Mandelbaume38c36b2019-10-11 14:43:46 +0000163 std::vector<DynTypedMatcher> Ms = buildMatchers(Rule);
164 assert(Ms.size() == 1 && "Cases must have compatible matchers.");
165 return Ms[0];
166}
167
Yitzhak Mandelbaum8bb47cd2019-10-16 01:06:46 +0000168SourceLocation transformer::detail::getRuleMatchLoc(const MatchResult &Result) {
Yitzhak Mandelbaume38c36b2019-10-11 14:43:46 +0000169 auto &NodesMap = Result.Nodes.getMap();
170 auto Root = NodesMap.find(RewriteRule::RootID);
171 assert(Root != NodesMap.end() && "Transformation failed: missing root node.");
Yitzhak Mandelbaum8bb47cd2019-10-16 01:06:46 +0000172 llvm::Optional<CharSourceRange> RootRange = tooling::getRangeForEdit(
Yitzhak Mandelbaume38c36b2019-10-11 14:43:46 +0000173 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`.
185const RewriteRule::Case &
Yitzhak Mandelbaum8bb47cd2019-10-16 01:06:46 +0000186transformer::detail::findSelectedCase(const MatchResult &Result,
Yitzhak Mandelbaume38c36b2019-10-11 14:43:46 +0000187 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
200constexpr llvm::StringLiteral RewriteRule::RootID;
Yitzhak Mandelbaum489449c2019-11-04 08:30:18 -0500201
202TextGenerator tooling::text(std::string M) {
203 return std::make_shared<SimpleTextGenerator>(std::move(M));
204}