xref: /llvm-project/clang-tools-extra/clangd/FeatureModule.h (revision f71ffd3b735b4d6ae3c12be1806cdd6205b3b378)
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 
23 namespace clang {
24 class CompilerInstance;
25 namespace clangd {
26 struct Diag;
27 class LSPBinder;
28 class SymbolIndex;
29 class ThreadsafeFS;
30 class TUScheduler;
31 class 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.
56 class FeatureModule {
57 public:
~FeatureModule()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.
initializeLSP(LSPBinder & Bind,const llvm::json::Object & ClientCaps,llvm::json::Object & ServerCaps)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.
stop()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.
blockUntilIdle(Deadline)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.
contributeTweaks(std::vector<std::unique_ptr<Tweak>> & Out)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.
beforeExecuteASTListener113     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.
sawDiagnosticASTListener118     virtual void sawDiagnostic(const clang::Diagnostic &, clangd::Diag &) {}
119   };
120   /// Can be called asynchronously before building an AST.
astListeners()121   virtual std::unique_ptr<ASTListener> astListeners() { return nullptr; }
122 
123 protected:
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.
scheduler()127   TUScheduler &scheduler() { return facilities().Scheduler; }
128   /// The index is used to get information about the whole codebase.
index()129   const SymbolIndex *index() { return facilities().Index; }
130   /// The filesystem is used to read source files on disk.
fs()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 
140 private:
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.
151 class 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 
164 public:
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>;
begin()170   iterator begin() { return iterator(Modules.begin()); }
end()171   iterator end() { return iterator(Modules.end()); }
begin()172   const_iterator begin() const { return const_iterator(Modules.begin()); }
end()173   const_iterator end() const { return const_iterator(Modules.end()); }
174 
add(std::unique_ptr<Mod> M)175   template <typename Mod> bool add(std::unique_ptr<Mod> M) {
176     return addImpl(&ID<Mod>::Key, std::move(M), LLVM_PRETTY_FUNCTION);
177   }
get()178   template <typename Mod> Mod *get() {
179     return static_cast<Mod *>(Map.lookup(&ID<Mod>::Key));
180   }
get()181   template <typename Mod> const Mod *get() const {
182     return const_cast<FeatureModuleSet *>(this)->get<Mod>();
183   }
184 };
185 
186 template <typename Mod> int FeatureModuleSet::ID<Mod>::Key;
187 
188 } // namespace clangd
189 } // namespace clang
190 #endif
191