1//===--- FeatureModule.h - Plugging features into clangd ----------*-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#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANGD_FEATUREMODULE_H
10#define LLVM_CLANG_TOOLS_EXTRA_CLANGD_FEATUREMODULE_H
11
12#include "support/Function.h"
13#include "support/Threading.h"
14#include "clang/Basic/Diagnostic.h"
15#include "llvm/ADT/FunctionExtras.h"
16#include "llvm/Support/Compiler.h"
17#include "llvm/Support/JSON.h"
18#include <memory>
19#include <optional>
20#include <type_traits>
21#include <vector>
22
23namespace clang {
24class CompilerInstance;
25namespace clangd {
26struct Diag;
27class LSPBinder;
28class SymbolIndex;
29class ThreadsafeFS;
30class TUScheduler;
31class Tweak;
32
33/// A FeatureModule contributes a vertical feature to clangd.
34///
35/// The lifetime of a module is roughly:
36/// - feature modules are created before the LSP server, in ClangdMain.cpp
37/// - these modules are then passed to ClangdLSPServer in a FeatureModuleSet
38/// - initializeLSP() is called when the editor calls initialize.
39// - initialize() is then called by ClangdServer as it is constructed.
40/// - module hooks can be called by the server at this point.
41/// Server facilities (scheduler etc) are available.
42/// - ClangdServer will not be destroyed until all the requests are done.
43/// FIXME: Block server shutdown until all the modules are idle.
44/// - When shutting down, ClangdServer will wait for all requests to
45/// finish, call stop(), and then blockUntilIdle().
46/// - feature modules will be destroyed after ClangdLSPServer is destroyed.
47///
48/// FeatureModules are not threadsafe in general. A module's entrypoints are:
49/// - method handlers registered in initializeLSP()
50/// - public methods called directly via ClangdServer.featureModule<T>()->...
51/// - specific overridable "hook" methods inherited from FeatureModule
52/// Unless otherwise specified, these are only called on the main thread.
53///
54/// Conventionally, standard feature modules live in the `clangd` namespace,
55/// and other exposed details live in a sub-namespace.
56class FeatureModule {
57public:
58 virtual ~FeatureModule() {
59 /// Perform shutdown sequence on destruction in case the ClangdServer was
60 /// never initialized. Usually redundant, but shutdown is idempotent.
61 stop();
62 blockUntilIdle(Deadline::infinity());
63 }
64
65 /// Called by the server to connect this feature module to LSP.
66 /// The module should register the methods/notifications/commands it handles,
67 /// and update the server capabilities to advertise them.
68 ///
69 /// This is only called if the module is running in ClangdLSPServer!
70 /// FeatureModules with a public interface should work without LSP bindings.
71 virtual void initializeLSP(LSPBinder &Bind,
72 const llvm::json::Object &ClientCaps,
73 llvm::json::Object &ServerCaps) {}
74
75 /// Shared server facilities needed by the module to get its work done.
76 struct Facilities {
77 TUScheduler &Scheduler;
78 const SymbolIndex *Index;
79 const ThreadsafeFS &FS;
80 };
81 /// Called by the server to prepare this module for use.
82 void initialize(const Facilities &F);
83
84 /// Requests that the module cancel background work and go idle soon.
85 /// Does not block, the caller will call blockUntilIdle() instead.
86 /// After a module is stop()ed, it should not receive any more requests.
87 /// Called by the server when shutting down.
88 /// May be called multiple times, should be idempotent.
89 virtual void stop() {}
90
91 /// Waits until the module is idle (no background work) or a deadline expires.
92 /// In general all modules should eventually go idle, though it may take a
93 /// long time (e.g. background indexing).
94 /// FeatureModules should go idle quickly if stop() has been called.
95 /// Called by the server when shutting down, and also by tests.
96 virtual bool blockUntilIdle(Deadline) { return true; }
97
98 /// Tweaks implemented by this module. Can be called asynchronously when
99 /// enumerating or applying code actions.
100 virtual void contributeTweaks(std::vector<std::unique_ptr<Tweak>> &Out) {}
101
102 /// Extension point that allows modules to observe and modify an AST build.
103 /// One instance is created each time clangd produces a ParsedAST or
104 /// PrecompiledPreamble. For a given instance, lifecycle methods are always
105 /// called on a single thread.
106 struct ASTListener {
107 /// Listeners are destroyed once the AST is built.
108 virtual ~ASTListener() = default;
109
110 /// Called before every AST build, both for main file and preamble. The call
111 /// happens immediately before FrontendAction::Execute(), with Preprocessor
112 /// set up already and after BeginSourceFile() on main file was called.
113 virtual void beforeExecute(CompilerInstance &CI) {}
114
115 /// Called everytime a diagnostic is encountered. Modules can use this
116 /// modify the final diagnostic, or store some information to surface code
117 /// actions later on.
118 virtual void sawDiagnostic(const clang::Diagnostic &, clangd::Diag &) {}
119 };
120 /// Can be called asynchronously before building an AST.
121 virtual std::unique_ptr<ASTListener> astListeners() { return nullptr; }
122
123protected:
124 /// Accessors for modules to access shared server facilities they depend on.
125 Facilities &facilities();
126 /// The scheduler is used to run tasks on worker threads and access ASTs.
127 TUScheduler &scheduler() { return facilities().Scheduler; }
128 /// The index is used to get information about the whole codebase.
129 const SymbolIndex *index() { return facilities().Index; }
130 /// The filesystem is used to read source files on disk.
131 const ThreadsafeFS &fs() { return facilities().FS; }
132
133 /// Types of function objects that feature modules use for outgoing calls.
134 /// (Bound throuh LSPBinder, made available here for convenience).
135 template <typename P>
136 using OutgoingNotification = llvm::unique_function<void(const P &)>;
137 template <typename P, typename R>
138 using OutgoingMethod = llvm::unique_function<void(const P &, Callback<R>)>;
139
140private:
141 std::optional<Facilities> Fac;
142};
143
144/// A FeatureModuleSet is a collection of feature modules installed in clangd.
145///
146/// Modules can be looked up by type, or used via the FeatureModule interface.
147/// This allows individual modules to expose a public API.
148/// For this reason, there can be only one feature module of each type.
149///
150/// The set owns the modules. It is itself owned by main, not ClangdServer.
151class FeatureModuleSet {
152 std::vector<std::unique_ptr<FeatureModule>> Modules;
153 llvm::DenseMap<void *, FeatureModule *> Map;
154
155 template <typename Mod> struct ID {
156 static_assert(std::is_base_of<FeatureModule, Mod>::value &&
157 std::is_final<Mod>::value,
158 "Modules must be final classes derived from clangd::Module");
159 static int Key;
160 };
161
162 bool addImpl(void *Key, std::unique_ptr<FeatureModule>, const char *Source);
163
164public:
165 FeatureModuleSet() = default;
166
167 using iterator = llvm::pointee_iterator<decltype(Modules)::iterator>;
168 using const_iterator =
169 llvm::pointee_iterator<decltype(Modules)::const_iterator>;
170 iterator begin() { return iterator(Modules.begin()); }
171 iterator end() { return iterator(Modules.end()); }
172 const_iterator begin() const { return const_iterator(Modules.begin()); }
173 const_iterator end() const { return const_iterator(Modules.end()); }
174
175 template <typename Mod> bool add(std::unique_ptr<Mod> M) {
176 return addImpl(Key: &ID<Mod>::Key, std::move(M), LLVM_PRETTY_FUNCTION);
177 }
178 template <typename Mod> Mod *get() {
179 return static_cast<Mod *>(Map.lookup(Val: &ID<Mod>::Key));
180 }
181 template <typename Mod> const Mod *get() const {
182 return const_cast<FeatureModuleSet *>(this)->get<Mod>();
183 }
184};
185
186template <typename Mod> int FeatureModuleSet::ID<Mod>::Key;
187
188} // namespace clangd
189} // namespace clang
190#endif
191

source code of clang-tools-extra/clangd/FeatureModule.h