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/Transformer.h" 10 #include "clang/AST/Expr.h" 11 #include "clang/ASTMatchers/ASTMatchFinder.h" 12 #include "clang/ASTMatchers/ASTMatchers.h" 13 #include "clang/Basic/Diagnostic.h" 14 #include "clang/Basic/SourceLocation.h" 15 #include "clang/Rewrite/Core/Rewriter.h" 16 #include "clang/Tooling/Refactoring/AtomicChange.h" 17 #include "clang/Tooling/Transformer/SourceCode.h" 18 #include "llvm/ADT/Optional.h" 19 #include "llvm/ADT/StringRef.h" 20 #include "llvm/Support/Errc.h" 21 #include "llvm/Support/Error.h" 22 #include <map> 23 #include <string> 24 #include <utility> 25 #include <vector> 26 27 using namespace clang; 28 using namespace tooling; 29 30 using ast_matchers::MatchFinder; 31 using ast_matchers::internal::DynTypedMatcher; 32 using ast_type_traits::ASTNodeKind; 33 using ast_type_traits::DynTypedNode; 34 using llvm::Error; 35 using llvm::StringError; 36 37 using MatchResult = MatchFinder::MatchResult; 38 39 Expected<SmallVector<tooling::detail::Transformation, 1>> 40 tooling::detail::translateEdits(const MatchResult &Result, 41 llvm::ArrayRef<ASTEdit> Edits) { 42 SmallVector<tooling::detail::Transformation, 1> Transformations; 43 for (const auto &Edit : Edits) { 44 Expected<CharSourceRange> Range = Edit.TargetRange(Result); 45 if (!Range) 46 return Range.takeError(); 47 llvm::Optional<CharSourceRange> EditRange = 48 getRangeForEdit(*Range, *Result.Context); 49 // FIXME: let user specify whether to treat this case as an error or ignore 50 // it as is currently done. 51 if (!EditRange) 52 return SmallVector<Transformation, 0>(); 53 auto Replacement = Edit.Replacement(Result); 54 if (!Replacement) 55 return Replacement.takeError(); 56 tooling::detail::Transformation T; 57 T.Range = *EditRange; 58 T.Replacement = std::move(*Replacement); 59 Transformations.push_back(std::move(T)); 60 } 61 return Transformations; 62 } 63 64 ASTEdit tooling::change(RangeSelector S, TextGenerator Replacement) { 65 ASTEdit E; 66 E.TargetRange = std::move(S); 67 E.Replacement = std::move(Replacement); 68 return E; 69 } 70 71 RewriteRule tooling::makeRule(DynTypedMatcher M, SmallVector<ASTEdit, 1> Edits, 72 TextGenerator Explanation) { 73 return RewriteRule{{RewriteRule::Case{ 74 std::move(M), std::move(Edits), std::move(Explanation), {}}}}; 75 } 76 77 void tooling::addInclude(RewriteRule &Rule, StringRef Header, 78 IncludeFormat Format) { 79 for (auto &Case : Rule.Cases) 80 Case.AddedIncludes.emplace_back(Header.str(), Format); 81 } 82 83 #ifndef NDEBUG 84 // Filters for supported matcher kinds. FIXME: Explicitly list the allowed kinds 85 // (all node matcher types except for `QualType` and `Type`), rather than just 86 // banning `QualType` and `Type`. 87 static bool hasValidKind(const DynTypedMatcher &M) { 88 return !M.canConvertTo<QualType>(); 89 } 90 #endif 91 92 // Binds each rule's matcher to a unique (and deterministic) tag based on 93 // `TagBase` and the id paired with the case. 94 static std::vector<DynTypedMatcher> taggedMatchers( 95 StringRef TagBase, 96 const SmallVectorImpl<std::pair<size_t, RewriteRule::Case>> &Cases) { 97 std::vector<DynTypedMatcher> Matchers; 98 Matchers.reserve(Cases.size()); 99 for (const auto &Case : Cases) { 100 std::string Tag = (TagBase + Twine(Case.first)).str(); 101 // HACK: Many matchers are not bindable, so ensure that tryBind will work. 102 DynTypedMatcher BoundMatcher(Case.second.Matcher); 103 BoundMatcher.setAllowBind(true); 104 auto M = BoundMatcher.tryBind(Tag); 105 Matchers.push_back(*std::move(M)); 106 } 107 return Matchers; 108 } 109 110 // Simply gathers the contents of the various rules into a single rule. The 111 // actual work to combine these into an ordered choice is deferred to matcher 112 // registration. 113 RewriteRule tooling::applyFirst(ArrayRef<RewriteRule> Rules) { 114 RewriteRule R; 115 for (auto &Rule : Rules) 116 R.Cases.append(Rule.Cases.begin(), Rule.Cases.end()); 117 return R; 118 } 119 120 std::vector<DynTypedMatcher> 121 tooling::detail::buildMatchers(const RewriteRule &Rule) { 122 // Map the cases into buckets of matchers -- one for each "root" AST kind, 123 // which guarantees that they can be combined in a single anyOf matcher. Each 124 // case is paired with an identifying number that is converted to a string id 125 // in `taggedMatchers`. 126 std::map<ASTNodeKind, SmallVector<std::pair<size_t, RewriteRule::Case>, 1>> 127 Buckets; 128 const SmallVectorImpl<RewriteRule::Case> &Cases = Rule.Cases; 129 for (int I = 0, N = Cases.size(); I < N; ++I) { 130 assert(hasValidKind(Cases[I].Matcher) && 131 "Matcher must be non-(Qual)Type node matcher"); 132 Buckets[Cases[I].Matcher.getSupportedKind()].emplace_back(I, Cases[I]); 133 } 134 135 std::vector<DynTypedMatcher> Matchers; 136 for (const auto &Bucket : Buckets) { 137 DynTypedMatcher M = DynTypedMatcher::constructVariadic( 138 DynTypedMatcher::VO_AnyOf, Bucket.first, 139 taggedMatchers("Tag", Bucket.second)); 140 M.setAllowBind(true); 141 // `tryBind` is guaranteed to succeed, because `AllowBind` was set to true. 142 Matchers.push_back(*M.tryBind(RewriteRule::RootID)); 143 } 144 return Matchers; 145 } 146 147 DynTypedMatcher tooling::detail::buildMatcher(const RewriteRule &Rule) { 148 std::vector<DynTypedMatcher> Ms = buildMatchers(Rule); 149 assert(Ms.size() == 1 && "Cases must have compatible matchers."); 150 return Ms[0]; 151 } 152 153 SourceLocation tooling::detail::getRuleMatchLoc(const MatchResult &Result) { 154 auto &NodesMap = Result.Nodes.getMap(); 155 auto Root = NodesMap.find(RewriteRule::RootID); 156 assert(Root != NodesMap.end() && "Transformation failed: missing root node."); 157 llvm::Optional<CharSourceRange> RootRange = getRangeForEdit( 158 CharSourceRange::getTokenRange(Root->second.getSourceRange()), 159 *Result.Context); 160 if (RootRange) 161 return RootRange->getBegin(); 162 // The match doesn't have a coherent range, so fall back to the expansion 163 // location as the "beginning" of the match. 164 return Result.SourceManager->getExpansionLoc( 165 Root->second.getSourceRange().getBegin()); 166 } 167 168 // Finds the case that was "selected" -- that is, whose matcher triggered the 169 // `MatchResult`. 170 const RewriteRule::Case & 171 tooling::detail::findSelectedCase(const MatchResult &Result, 172 const RewriteRule &Rule) { 173 if (Rule.Cases.size() == 1) 174 return Rule.Cases[0]; 175 176 auto &NodesMap = Result.Nodes.getMap(); 177 for (size_t i = 0, N = Rule.Cases.size(); i < N; ++i) { 178 std::string Tag = ("Tag" + Twine(i)).str(); 179 if (NodesMap.find(Tag) != NodesMap.end()) 180 return Rule.Cases[i]; 181 } 182 llvm_unreachable("No tag found for this rule."); 183 } 184 185 constexpr llvm::StringLiteral RewriteRule::RootID; 186 187 void Transformer::registerMatchers(MatchFinder *MatchFinder) { 188 for (auto &Matcher : tooling::detail::buildMatchers(Rule)) 189 MatchFinder->addDynamicMatcher(Matcher, this); 190 } 191 192 void Transformer::run(const MatchResult &Result) { 193 if (Result.Context->getDiagnostics().hasErrorOccurred()) 194 return; 195 196 RewriteRule::Case Case = tooling::detail::findSelectedCase(Result, Rule); 197 auto Transformations = tooling::detail::translateEdits(Result, Case.Edits); 198 if (!Transformations) { 199 Consumer(Transformations.takeError()); 200 return; 201 } 202 203 if (Transformations->empty()) { 204 // No rewrite applied (but no error encountered either). 205 detail::getRuleMatchLoc(Result).print( 206 llvm::errs() << "note: skipping match at loc ", *Result.SourceManager); 207 llvm::errs() << "\n"; 208 return; 209 } 210 211 // Record the results in the AtomicChange, anchored at the location of the 212 // first change. 213 AtomicChange AC(*Result.SourceManager, 214 (*Transformations)[0].Range.getBegin()); 215 for (const auto &T : *Transformations) { 216 if (auto Err = AC.replace(*Result.SourceManager, T.Range, T.Replacement)) { 217 Consumer(std::move(Err)); 218 return; 219 } 220 } 221 222 for (const auto &I : Case.AddedIncludes) { 223 auto &Header = I.first; 224 switch (I.second) { 225 case IncludeFormat::Quoted: 226 AC.addHeader(Header); 227 break; 228 case IncludeFormat::Angled: 229 AC.addHeader((llvm::Twine("<") + Header + ">").str()); 230 break; 231 } 232 } 233 234 Consumer(std::move(AC)); 235 } 236