clang-tools 24.0.0git
FeatureModule.h
Go to the documentation of this file.
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 "llvm/Support/Registry.h"
19#include <memory>
20#include <optional>
21#include <type_traits>
22#include <vector>
23
24namespace clang {
25class CompilerInstance;
26namespace clangd {
27struct Diag;
28class LSPBinder;
29class SymbolIndex;
30class ThreadsafeFS;
31class TUScheduler;
32class Tweak;
33
34/// A FeatureModule contributes a vertical feature to clangd.
35///
36/// The lifetime of a module is roughly:
37/// - feature modules are created before the LSP server, in ClangdMain.cpp
38/// - these modules are then passed to ClangdLSPServer in a FeatureModuleSet
39/// - initializeLSP() is called when the editor calls initialize.
40// - initialize() is then called by ClangdServer as it is constructed.
41/// - module hooks can be called by the server at this point.
42/// Server facilities (scheduler etc) are available.
43/// - ClangdServer will not be destroyed until all the requests are done.
44/// FIXME: Block server shutdown until all the modules are idle.
45/// - When shutting down, ClangdServer will wait for all requests to
46/// finish, call stop(), and then blockUntilIdle().
47/// - feature modules will be destroyed after ClangdLSPServer is destroyed.
48///
49/// FeatureModules are not threadsafe in general. A module's entrypoints are:
50/// - method handlers registered in initializeLSP()
51/// - public methods called directly via ClangdServer.featureModule<T>()->...
52/// - specific overridable "hook" methods inherited from FeatureModule
53/// Unless otherwise specified, these are only called on the main thread.
54///
55/// Conventionally, standard feature modules live in the `clangd` namespace,
56/// and other exposed details live in a sub-namespace.
58public:
59 virtual ~FeatureModule() {
60 /// Perform shutdown sequence on destruction in case the ClangdServer was
61 /// never initialized. Usually redundant, but shutdown is idempotent.
62 stop();
64 }
65
66 /// Called by the server to connect this feature module to LSP.
67 /// The module should register the methods/notifications/commands it handles,
68 /// and update the server capabilities to advertise them.
69 ///
70 /// This is only called if the module is running in ClangdLSPServer!
71 /// FeatureModules with a public interface should work without LSP bindings.
72 virtual void initializeLSP(LSPBinder &Bind,
73 const llvm::json::Object &ClientCaps,
74 llvm::json::Object &ServerCaps) {}
75
76 /// Shared server facilities needed by the module to get its work done.
82 /// Called by the server to prepare this module for use.
83 void initialize(const Facilities &F);
84
85 /// Requests that the module cancel background work and go idle soon.
86 /// Does not block, the caller will call blockUntilIdle() instead.
87 /// After a module is stop()ed, it should not receive any more requests.
88 /// Called by the server when shutting down.
89 /// May be called multiple times, should be idempotent.
90 virtual void stop() {}
91
92 /// Waits until the module is idle (no background work) or a deadline expires.
93 /// In general all modules should eventually go idle, though it may take a
94 /// long time (e.g. background indexing).
95 /// FeatureModules should go idle quickly if stop() has been called.
96 /// Called by the server when shutting down, and also by tests.
97 virtual bool blockUntilIdle(Deadline) { return true; }
98
99 /// Tweaks implemented by this module. Can be called asynchronously when
100 /// enumerating or applying code actions.
101 virtual void contributeTweaks(std::vector<std::unique_ptr<Tweak>> &Out) {}
102
103 /// Extension point that allows modules to observe and modify an AST build.
104 /// One instance is created each time clangd produces a ParsedAST or
105 /// PrecompiledPreamble. For a given instance, lifecycle methods are always
106 /// called on a single thread.
107 struct ASTListener {
108 /// Listeners are destroyed once the AST is built.
109 virtual ~ASTListener() = default;
110
111 /// Called for a main-file build immediately before BeginSourceFile(). Not
112 /// called for preamble builds. The virtual filesystem, diagnostics, target,
113 /// and file manager are available. The source manager, preprocessor, AST
114 /// context, and AST consumer are not set up yet.
115 /// Use this to configure diagnostics emitted during frontend
116 /// initialization, including loading precompiled modules. Normal diagnostic
117 /// filtering still applies. Do not change the frontend inputs or options
118 /// affecting compilation semantics (e.g. macro definitions): they must
119 /// remain consistent with the preamble, which may already have been built.
120 virtual void beforeBeginSourceFile(CompilerInstance &CI) {}
121
122 /// Called before every AST build, after the Preprocessor and ASTConsumer
123 /// are set up, but before clangd installs its include and macro collectors.
124 /// Modules should only use this when their PPCallbacks must observe
125 /// preamble events replayed during a main-file build.
126 /// Reusing a preamble skips the main file's initial preprocessing
127 /// directives. ReplayPreamble synthesizes callbacks for selected events
128 /// from that region and captures their recipients before beforeExecute() is
129 /// called.
130 virtual void beforePPCallbacks(CompilerInstance &CI) {}
131
132 /// Called before every AST build, both for main file and preamble. The call
133 /// happens immediately before FrontendAction::Execute(), with Preprocessor
134 /// set up already and after BeginSourceFile() on main file was called.
135 virtual void beforeExecute(CompilerInstance &CI) {}
136
137 /// Called after FrontendAction::Execute() for a main-file build, once
138 /// clangd has collected tokens and restricted the AST traversal scope.
139 /// The preprocessor has not received EndSourceFile() yet.
140 virtual void afterExecute(CompilerInstance &CI) {}
141
142 /// Called everytime a diagnostic is encountered. Modules can use this
143 /// modify the final diagnostic, or store some information to surface code
144 /// actions later on.
145 virtual void sawDiagnostic(const clang::Diagnostic &, clangd::Diag &) {}
146
147 /// Called after a diagnostic is fully assembled, including notes and
148 /// fixes, and before it is returned to the caller.
150 };
151 /// Can be called asynchronously before building an AST.
152 virtual std::unique_ptr<ASTListener> astListeners() { return nullptr; }
153
154protected:
155 /// Accessors for modules to access shared server facilities they depend on.
156 Facilities &facilities();
157 /// The scheduler is used to run tasks on worker threads and access ASTs.
159 /// The index is used to get information about the whole codebase.
160 const SymbolIndex *index() { return facilities().Index; }
161 /// The filesystem is used to read source files on disk.
162 const ThreadsafeFS &fs() { return facilities().FS; }
163
164 /// Types of function objects that feature modules use for outgoing calls.
165 /// (Bound throuh LSPBinder, made available here for convenience).
166 template <typename P>
167 using OutgoingNotification = llvm::unique_function<void(const P &)>;
168 template <typename P, typename R>
169 using OutgoingMethod = llvm::unique_function<void(const P &, Callback<R>)>;
170
171private:
172 std::optional<Facilities> Fac;
173};
174
175/// A FeatureModuleSet is a collection of feature modules installed in clangd.
176///
177/// Modules added with explicit type specification can be looked up by type, or
178/// used via the FeatureModule interface. This allows individual modules to
179/// expose a public API. For this reason, there can be only one feature module
180/// of each type.
181///
182/// Modules added using a base class pointer can be used only via the
183/// FeatureModule interface and can't be looked up by type, thus custom public
184/// API (if provided by the module) can't be used.
185///
186/// The set owns the modules. It is itself owned by main, not ClangdServer.
188 std::vector<std::unique_ptr<FeatureModule>> Modules;
189 llvm::DenseMap<void *, FeatureModule *> Map;
190
191 template <typename Mod> struct ID {
192 static_assert(std::is_base_of<FeatureModule, Mod>::value &&
193 std::is_final<Mod>::value,
194 "Modules must be final classes derived from clangd::Module");
195 static int Key;
196 };
197
198 bool addImpl(void *Key, std::unique_ptr<FeatureModule>, const char *Source);
199
200public:
201 FeatureModuleSet() = default;
202
204
205 using iterator = llvm::pointee_iterator<decltype(Modules)::iterator>;
207 llvm::pointee_iterator<decltype(Modules)::const_iterator>;
208 iterator begin() { return iterator(Modules.begin()); }
209 iterator end() { return iterator(Modules.end()); }
210 const_iterator begin() const { return const_iterator(Modules.begin()); }
211 const_iterator end() const { return const_iterator(Modules.end()); }
212
213 void add(std::unique_ptr<FeatureModule> M);
214 template <typename Mod> bool add(std::unique_ptr<Mod> M) {
215 return addImpl(&ID<Mod>::Key, std::move(M), LLVM_PRETTY_FUNCTION);
216 }
217 template <typename Mod> Mod *get() {
218 return static_cast<Mod *>(Map.lookup(&ID<Mod>::Key));
219 }
220 template <typename Mod> const Mod *get() const {
221 return const_cast<FeatureModuleSet *>(this)->get<Mod>();
222 }
223};
224
225template <typename Mod> int FeatureModuleSet::ID<Mod>::Key;
226
227using FeatureModuleRegistry = llvm::Registry<FeatureModule>;
228
229} // namespace clangd
230} // namespace clang
231
232namespace llvm {
233extern template class Registry<clang::clangd::FeatureModule>;
234} // namespace llvm
235
236#endif
A point in time we can wait for.
Definition Threading.h:46
static Deadline infinity()
Definition Threading.h:51
llvm::pointee_iterator< decltype(Modules)::iterator > iterator
bool add(std::unique_ptr< Mod > M)
static FeatureModuleSet fromRegistry()
const_iterator begin() const
void add(std::unique_ptr< FeatureModule > M)
llvm::pointee_iterator< decltype(Modules)::const_iterator > const_iterator
const_iterator end() const
A FeatureModule contributes a vertical feature to clangd.
virtual std::unique_ptr< ASTListener > astListeners()
Can be called asynchronously before building an AST.
const SymbolIndex * index()
The index is used to get information about the whole codebase.
Facilities & facilities()
Accessors for modules to access shared server facilities they depend on.
void initialize(const Facilities &F)
Called by the server to prepare this module for use.
TUScheduler & scheduler()
The scheduler is used to run tasks on worker threads and access ASTs.
llvm::unique_function< void(const P &, Callback< R >)> OutgoingMethod
llvm::unique_function< void(const P &)> OutgoingNotification
Types of function objects that feature modules use for outgoing calls.
const ThreadsafeFS & fs()
The filesystem is used to read source files on disk.
virtual void stop()
Requests that the module cancel background work and go idle soon.
virtual bool blockUntilIdle(Deadline)
Waits until the module is idle (no background work) or a deadline expires.
virtual void initializeLSP(LSPBinder &Bind, const llvm::json::Object &ClientCaps, llvm::json::Object &ServerCaps)
Called by the server to connect this feature module to LSP.
virtual void contributeTweaks(std::vector< std::unique_ptr< Tweak > > &Out)
Tweaks implemented by this module.
Values in a Context are indexed by typed keys.
Definition Context.h:40
LSPBinder collects a table of functions that handle LSP calls.
Definition LSPBinder.h:34
Interface for symbol indexes that can be used for searching or matching symbols among a set of symbol...
Definition Index.h:134
Handles running tasks for ClangdServer and managing the resources (e.g., preambles and ASTs) for open...
Wrapper for vfs::FileSystem for use in multithreaded programs like clangd.
An interface base for small context-sensitive refactoring actions.
Definition Tweak.h:46
FIXME: Skip testing on windows temporarily due to the different escaping code mode.
Definition AST.cpp:44
llvm::Registry< FeatureModule > FeatureModuleRegistry
llvm::unique_function< void(llvm::Expected< T >)> Callback
A Callback<T> is a void function that accepts Expected<T>.
Definition Function.h:28
===– Representation.cpp - ClangDoc Representation --------—*- C++ -*-===//
Some operations such as code completion produce a set of candidates.
Definition Generators.h:150
A top-level diagnostic that may have Notes and Fixes.
Definition Diagnostics.h:98
Extension point that allows modules to observe and modify an AST build.
virtual void sawDiagnostic(const clang::Diagnostic &, clangd::Diag &)
Called everytime a diagnostic is encountered.
virtual ~ASTListener()=default
Listeners are destroyed once the AST is built.
virtual void beforeExecute(CompilerInstance &CI)
Called before every AST build, both for main file and preamble.
virtual void afterExecute(CompilerInstance &CI)
Called after FrontendAction::Execute() for a main-file build, once clangd has collected tokens and re...
virtual void finalizeDiagnostic(clangd::Diag &)
Called after a diagnostic is fully assembled, including notes and fixes, and before it is returned to...
virtual void beforeBeginSourceFile(CompilerInstance &CI)
Called for a main-file build immediately before BeginSourceFile().
virtual void beforePPCallbacks(CompilerInstance &CI)
Called before every AST build, after the Preprocessor and ASTConsumer are set up, but before clangd i...
Shared server facilities needed by the module to get its work done.