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/ASTMatchers/ASTMatchFinder.h" 11 #include "clang/ASTMatchers/ASTMatchersInternal.h" 12 #include "clang/Basic/SourceLocation.h" 13 #include "clang/Tooling/Refactoring/AtomicChange.h" 14 #include "llvm/Support/Error.h" 15 #include <map> 16 #include <utility> 17 #include <vector> 18 19 using namespace clang; 20 using namespace tooling; 21 22 using ast_matchers::MatchFinder; 23 24 void Transformer::registerMatchers(MatchFinder *MatchFinder) { 25 for (auto &Matcher : transformer::detail::buildMatchers(Rule)) 26 MatchFinder->addDynamicMatcher(Matcher, this); 27 } 28 29 void Transformer::run(const MatchFinder::MatchResult &Result) { 30 if (Result.Context->getDiagnostics().hasErrorOccurred()) 31 return; 32 33 transformer::RewriteRule::Case Case = 34 transformer::detail::findSelectedCase(Result, Rule); 35 auto Transformations = Case.Edits(Result); 36 if (!Transformations) { 37 Consumer(Transformations.takeError()); 38 return; 39 } 40 41 if (Transformations->empty()) { 42 // No rewrite applied (but no error encountered either). 43 transformer::detail::getRuleMatchLoc(Result).print( 44 llvm::errs() << "note: skipping match at loc ", *Result.SourceManager); 45 llvm::errs() << "\n"; 46 return; 47 } 48 49 // Group the transformations, by file, into AtomicChanges, each anchored by 50 // the location of the first change in that file. 51 std::map<FileID, AtomicChange> ChangesByFileID; 52 for (const auto &T : *Transformations) { 53 auto ID = Result.SourceManager->getFileID(T.Range.getBegin()); 54 auto Iter = ChangesByFileID 55 .emplace(ID, AtomicChange(*Result.SourceManager, 56 T.Range.getBegin(), T.Metadata)) 57 .first; 58 auto &AC = Iter->second; 59 if (auto Err = AC.replace(*Result.SourceManager, T.Range, T.Replacement)) { 60 Consumer(std::move(Err)); 61 return; 62 } 63 } 64 65 for (auto &IDChangePair : ChangesByFileID) { 66 auto &AC = IDChangePair.second; 67 // FIXME: this will add includes to *all* changed files, which may not be 68 // the intent. We should upgrade the representation to allow associating 69 // headers with specific edits. 70 for (const auto &I : Case.AddedIncludes) { 71 auto &Header = I.first; 72 switch (I.second) { 73 case transformer::IncludeFormat::Quoted: 74 AC.addHeader(Header); 75 break; 76 case transformer::IncludeFormat::Angled: 77 AC.addHeader((llvm::Twine("<") + Header + ">").str()); 78 break; 79 } 80 } 81 82 Consumer(std::move(AC)); 83 } 84 } 85