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/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 NDEBUG
36 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#endif
45}
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(args: E.instantiate());
52 if (Modules) {
53 for (auto &M : *Modules)
54 M.contributeTweaks(Out&: All);
55 }
56 return All;
57}
58} // namespace
59
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(FID: SM.getMainFileID());
68 Cursor = SM.getComposedLoc(FID: SM.getMainFileID(), Offset: 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(Sel: S))
80 continue;
81 Available.push_back(x: std::move(T));
82 }
83 // Ensure deterministic order of the results.
84 llvm::sort(C&: Available,
85 Comp: [](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(Sel: S))
97 return error(Fmt: "failed to prepare() tweak {0}", Vals&: ID);
98 return std::move(T);
99 }
100 return error(Fmt: "tweak ID {0} is invalid", Vals&: 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(F: *FE, FileMgr&: SM.getFileManager()))
109 return std::make_pair(x&: *FilePath, y: std::move(Ed));
110 return error(Fmt: "Failed to get absolute path for edited file: {0}",
111 Vals: 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, FID: SM.getMainFileID(), Replacements: std::move(Replacements));
118 if (!PathAndEdit)
119 return PathAndEdit.takeError();
120 Tweak::Effect E;
121 E.ApplyEdits.try_emplace(Key: PathAndEdit->first, Args&: PathAndEdit->second);
122 return E;
123}
124
125} // namespace clangd
126} // namespace clang
127

source code of clang-tools-extra/clangd/refactor/Tweak.cpp