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