//===--- Tweak.cpp -----------------------------------------------*- C++-*-===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// #include "Tweak.h" #include "FeatureModule.h" #include "SourceCode.h" #include "index/Index.h" #include "support/Logger.h" #include "support/Path.h" #include "llvm/ADT/STLExtras.h" #include "llvm/ADT/StringMap.h" #include "llvm/ADT/StringRef.h" #include "llvm/Support/Error.h" #include "llvm/Support/Registry.h" #include #include #include #include LLVM_INSTANTIATE_REGISTRY(llvm::Registry) namespace clang { namespace clangd { /// A handy typedef to save some typing. typedef llvm::Registry TweakRegistry; namespace { /// Asserts invariants on TweakRegistry. No-op with assertion disabled. void validateRegistry() { #ifndef NDEBUG llvm::StringSet<> Seen; for (const auto &E : TweakRegistry::entries()) { // REGISTER_TWEAK ensures E.getName() is equal to the tweak class name. // We check that id() matches it. assert(E.instantiate()->id() == E.getName() && "id should be equal to class name"); assert(Seen.try_emplace(E.getName()).second && "duplicate check id"); } #endif } std::vector> getAllTweaks(const FeatureModuleSet *Modules) { std::vector> All; for (const auto &E : TweakRegistry::entries()) All.emplace_back(E.instantiate()); if (Modules) { for (auto &M : *Modules) M.contributeTweaks(All); } return All; } } // namespace Tweak::Selection::Selection(const SymbolIndex *Index, ParsedAST &AST, unsigned RangeBegin, unsigned RangeEnd, SelectionTree ASTSelection, llvm::vfs::FileSystem *FS) : Index(Index), AST(&AST), SelectionBegin(RangeBegin), SelectionEnd(RangeEnd), ASTSelection(std::move(ASTSelection)), FS(FS) { auto &SM = AST.getSourceManager(); Code = SM.getBufferData(SM.getMainFileID()); Cursor = SM.getComposedLoc(SM.getMainFileID(), RangeBegin); } std::vector> prepareTweaks(const Tweak::Selection &S, llvm::function_ref Filter, const FeatureModuleSet *Modules) { validateRegistry(); std::vector> Available; for (auto &T : getAllTweaks(Modules)) { if (!Filter(*T) || !T->prepare(S)) continue; Available.push_back(std::move(T)); } // Ensure deterministic order of the results. llvm::sort(Available, [](const std::unique_ptr &L, const std::unique_ptr &R) { return L->id() < R->id(); }); return Available; } llvm::Expected> prepareTweak(StringRef ID, const Tweak::Selection &S, const FeatureModuleSet *Modules) { for (auto &T : getAllTweaks(Modules)) { if (T->id() != ID) continue; if (!T->prepare(S)) return error("failed to prepare() tweak {0}", ID); return std::move(T); } return error("tweak ID {0} is invalid", ID); } llvm::Expected> Tweak::Effect::fileEdit(const SourceManager &SM, FileID FID, tooling::Replacements Replacements) { Edit Ed(SM.getBufferData(FID), std::move(Replacements)); if (const auto FE = SM.getFileEntryRefForID(FID)) if (auto FilePath = getCanonicalPath(*FE, SM.getFileManager())) return std::make_pair(*FilePath, std::move(Ed)); return error("Failed to get absolute path for edited file: {0}", SM.getFileEntryRefForID(FID)->getName()); } llvm::Expected Tweak::Effect::mainFileEdit(const SourceManager &SM, tooling::Replacements Replacements) { auto PathAndEdit = fileEdit(SM, SM.getMainFileID(), std::move(Replacements)); if (!PathAndEdit) return PathAndEdit.takeError(); Tweak::Effect E; E.ApplyEdits.try_emplace(PathAndEdit->first, PathAndEdit->second); return E; } } // namespace clangd } // namespace clang