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