RewriteRule.cpp revision 1.1.1.1
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 23using namespace clang; 24using namespace transformer; 25 26using ast_matchers::MatchFinder; 27using ast_matchers::internal::DynTypedMatcher; 28using ast_type_traits::ASTNodeKind; 29 30using MatchResult = MatchFinder::MatchResult; 31 32Expected<SmallVector<transformer::detail::Transformation, 1>> 33transformer::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 57ASTEdit 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 64RewriteRule 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 70void 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`. 80static 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. 87static 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. 106RewriteRule 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 113std::vector<DynTypedMatcher> 114transformer::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 140DynTypedMatcher 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 146SourceLocation 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`. 163const RewriteRule::Case & 164transformer::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 178constexpr llvm::StringLiteral RewriteRule::RootID; 179