1 //===--- Tweak.cpp -----------------------------------------------*- 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 #include "Tweak.h" 9 #include "FeatureModule.h" 10 #include "SourceCode.h" 11 #include "index/Index.h" 12 #include "support/Logger.h" 13 #include "support/Path.h" 14 #include "llvm/ADT/None.h" 15 #include "llvm/ADT/Optional.h" 16 #include "llvm/ADT/STLExtras.h" 17 #include "llvm/ADT/StringMap.h" 18 #include "llvm/ADT/StringRef.h" 19 #include "llvm/Support/Error.h" 20 #include "llvm/Support/Registry.h" 21 #include <functional> 22 #include <memory> 23 #include <utility> 24 #include <vector> 25 26 LLVM_INSTANTIATE_REGISTRY(llvm::Registry<clang::clangd::Tweak>) 27 28 namespace clang { 29 namespace clangd { 30 31 /// A handy typedef to save some typing. 32 typedef llvm::Registry<Tweak> TweakRegistry; 33 34 namespace { 35 /// Asserts invariants on TweakRegistry. No-op with assertion disabled. 36 void validateRegistry() { 37 #ifndef NDEBUG 38 llvm::StringSet<> Seen; 39 for (const auto &E : TweakRegistry::entries()) { 40 // REGISTER_TWEAK ensures E.getName() is equal to the tweak class name. 41 // We check that id() matches it. 42 assert(E.instantiate()->id() == E.getName() && 43 "id should be equal to class name"); 44 assert(Seen.try_emplace(E.getName()).second && "duplicate check id"); 45 } 46 #endif 47 } 48 49 std::vector<std::unique_ptr<Tweak>> 50 getAllTweaks(const FeatureModuleSet *Modules) { 51 std::vector<std::unique_ptr<Tweak>> All; 52 for (const auto &E : TweakRegistry::entries()) 53 All.emplace_back(E.instantiate()); 54 if (Modules) { 55 for (auto &M : *Modules) 56 M.contributeTweaks(All); 57 } 58 return All; 59 } 60 } // namespace 61 62 Tweak::Selection::Selection(const SymbolIndex *Index, ParsedAST &AST, 63 unsigned RangeBegin, unsigned RangeEnd, 64 SelectionTree ASTSelection, 65 llvm::vfs::FileSystem *FS) 66 : Index(Index), AST(&AST), SelectionBegin(RangeBegin), 67 SelectionEnd(RangeEnd), ASTSelection(std::move(ASTSelection)), FS(FS) { 68 auto &SM = AST.getSourceManager(); 69 Code = SM.getBufferData(SM.getMainFileID()); 70 Cursor = SM.getComposedLoc(SM.getMainFileID(), RangeBegin); 71 } 72 73 std::vector<std::unique_ptr<Tweak>> 74 prepareTweaks(const Tweak::Selection &S, 75 llvm::function_ref<bool(const Tweak &)> Filter, 76 const FeatureModuleSet *Modules) { 77 validateRegistry(); 78 79 std::vector<std::unique_ptr<Tweak>> Available; 80 for (auto &T : getAllTweaks(Modules)) { 81 if (!Filter(*T) || !T->prepare(S)) 82 continue; 83 Available.push_back(std::move(T)); 84 } 85 // Ensure deterministic order of the results. 86 llvm::sort(Available, 87 [](const std::unique_ptr<Tweak> &L, 88 const std::unique_ptr<Tweak> &R) { return L->id() < R->id(); }); 89 return Available; 90 } 91 92 llvm::Expected<std::unique_ptr<Tweak>> 93 prepareTweak(StringRef ID, const Tweak::Selection &S, 94 const FeatureModuleSet *Modules) { 95 for (auto &T : getAllTweaks(Modules)) { 96 if (T->id() != ID) 97 continue; 98 if (!T->prepare(S)) 99 return error("failed to prepare() tweak {0}", ID); 100 return std::move(T); 101 } 102 return error("tweak ID {0} is invalid", ID); 103 } 104 105 llvm::Expected<std::pair<Path, Edit>> 106 Tweak::Effect::fileEdit(const SourceManager &SM, FileID FID, 107 tooling::Replacements Replacements) { 108 Edit Ed(SM.getBufferData(FID), std::move(Replacements)); 109 if (auto FilePath = getCanonicalPath(SM.getFileEntryForID(FID), SM)) 110 return std::make_pair(*FilePath, std::move(Ed)); 111 return error("Failed to get absolute path for edited file: {0}", 112 SM.getFileEntryForID(FID)->getName()); 113 } 114 115 llvm::Expected<Tweak::Effect> 116 Tweak::Effect::mainFileEdit(const SourceManager &SM, 117 tooling::Replacements Replacements) { 118 auto PathAndEdit = fileEdit(SM, SM.getMainFileID(), std::move(Replacements)); 119 if (!PathAndEdit) 120 return PathAndEdit.takeError(); 121 Tweak::Effect E; 122 E.ApplyEdits.try_emplace(PathAndEdit->first, PathAndEdit->second); 123 return E; 124 } 125 126 } // namespace clangd 127 } // namespace clang 128