mirror of
https://github.com/clangd/clangd.git
synced 2025-06-08 01:04:57 +00:00
Destroyed code walkthrough (markdown)
parent
1bbf536d9b
commit
48e9bd7fe8
@ -1,335 +0,0 @@
|
||||
# clangd code walkthrough
|
||||
|
||||
This describes the critical parts of clangd and roughly what they do.
|
||||
It may get out of date from time to time, please
|
||||
[file a bug](https://github.com/llvm/clangd-www/issues)!
|
||||
It mostly starts at the outside and works it way inward.
|
||||
|
||||
The clangd code lives in the [llvm-project] repository under
|
||||
[clang-tools-extra/clangd][clangd]. We'll also mention some dependencies in
|
||||
other parts of llvm-project (such as clang).
|
||||
|
||||
Links below point to the [woboq] code browser. It has nice cross-referenced navigation, but may show
|
||||
a slightly outdated version of the code.
|
||||
|
||||
## Starting up and managing files
|
||||
|
||||
### Entry point and JSON-RPC
|
||||
|
||||
The `clangd` binary itself has its [main()] entrypoint in `tool/ClangdMain.cpp`.
|
||||
This mostly parses flags and hooks `ClangdLSPServer` up to its dependencies.
|
||||
|
||||
One vital dependency is [JSONTransport] which speaks the JSON-RPC protocol
|
||||
over stdin/stdout. LSP is layered on top of this [Transport] abstraction.
|
||||
(There's also an Apple XPC transport in the [xpc/] directory).
|
||||
|
||||
We call `ClangdLSPServer.run()` to start the loop, and it synchronously
|
||||
processes messages until the client disconnects. Calls to the large
|
||||
non-threadsafe singletons (`ClangdLSPServer`, `ClangdServer`, `TUScheduler`)
|
||||
all happen on the main thread.
|
||||
See [threads and request handling].
|
||||
|
||||
### Language Server Protocol
|
||||
|
||||
[ClangdLSPServer] handles the LSP protocol details. Incoming requests are routed
|
||||
to some method on this class using a lookup table, and then implemented
|
||||
by dispatching them to the contained `ClangdServer`.
|
||||
|
||||
The incoming JSON requests are mapped onto structs defined in [Protocol.h].
|
||||
In the simplest cases these are just forwarded to the appropriate method on
|
||||
`ClangdServer` - we use the LSP structs as vocabulary types for most things.
|
||||
|
||||
In other cases there's some gap between LSP and what seems to be a sensible C++
|
||||
API, so `ClangdLSPServer` has some real work to do.
|
||||
|
||||
### ClangdServer and TUScheduler
|
||||
|
||||
The [ClangdServer] class is best thought of as the C++ API to clangd.
|
||||
Features tend to be implemented as stateless, synchronous functions ("give me
|
||||
hover information from this AST at offset 25"). ClangdServer exposes them as
|
||||
stateful, asynchronous functions ("compute hover information for the latest
|
||||
version of Foo.cpp at offset 25, call back when done") which is the LSP model.
|
||||
|
||||
[TUScheduler] is responsible for keeping track of the latest version of each
|
||||
file, building and caching ASTs and preambles as inputs, and providing threads
|
||||
to run requests on in an appropriate sequence. (More details in
|
||||
[threads and request handling]).
|
||||
It also pushes certain events to `ClangdServer` via [ParsingCallbacks], to
|
||||
allow emitting diagnostics and indexing ASTs.
|
||||
|
||||
`ClangdServer` is fairly mechanical for the most part. The features are
|
||||
implemented in various other files, and the scheduling and AST building is
|
||||
done by `TUScheduler`, so largely it just binds these together.
|
||||
`TUScheduler` doesn't know about particular features (except diagnostics).
|
||||
|
||||
### Compile commands
|
||||
|
||||
Like other clang-based tools, clangd uses clang's command-line syntax as its
|
||||
interface to configure parse options (like include directories).
|
||||
The arguments are obtained from a [tooling::CompilationDatabase], typically
|
||||
built by reading `compile_commands.json` from a nearby directory.
|
||||
[GlobalCompilationDatabase] is responsible for finding and caching such
|
||||
databases, and for providing "fallback" commands when none are found.
|
||||
|
||||
Various heuristic tweaks are applied to these commands to make them more likely
|
||||
to work, particularly on Mac. These live in [CommandMangler].
|
||||
|
||||
## Features
|
||||
|
||||
### Diagnostics
|
||||
|
||||
During parsing, clang emits diagnostics through a `DiagnosticConsumer` callback.
|
||||
clangd's [StoreDiags] implementation converts them into [Diag] objects,
|
||||
which capture the relationships between diagnostics, fixes, and notes.
|
||||
These are exposed in `ParsedAST`.
|
||||
(clang-tidy diagnostics are generated separately in `buildAST()`, but also end
|
||||
up captured by `StoreDiags` and exposed in the same way).
|
||||
|
||||
[IncludeFixer] attempts to add automatic fixes to certain diagnostics by using
|
||||
the index to find headers that should be included.
|
||||
|
||||
`TUScheduler` has a logic to determine when a `ParsedAST`'s diagnostics are
|
||||
"correct enough" to emit, and when a new build is needed for this purpose.
|
||||
It then triggers `ClangdServer::onMainAST`, which calls
|
||||
`ClangdLSPServer::onDiagnosticsReady`, which sends them to the client.
|
||||
|
||||
### AST-based features
|
||||
|
||||
Most clangd requests are handled by inspecting a `ParsedAST`, and maybe the
|
||||
index. Examples are [locateSymbolAt()] (go-to-definition) and [getHover()].
|
||||
|
||||
These features are spread across various files, but are easy to find from their
|
||||
callsites in `ClangdServer`.
|
||||
|
||||
### Code completion (and signature help)
|
||||
|
||||
Code completion does not follow the usual pattern for AST-based features.
|
||||
Instead there's a dedicated parse of the current file with a callback when the
|
||||
completion point is reached.
|
||||
The core completion logic is implemented in clang's [SemaCodeComplete.cpp] and
|
||||
has access to information not present in the AST, such as name-lookup structures
|
||||
and parser state.
|
||||
|
||||
[CodeComplete.h] is mostly concerned with running clang in this mode,
|
||||
combining clang's results with index-based results, applying ranking, and
|
||||
converting to LSP's data model.
|
||||
|
||||
The ranking is mostly implemented in [Quality.h], and the name-matching
|
||||
is done by [FuzzyMatcher].
|
||||
|
||||
### Code actions
|
||||
|
||||
Most code actions are provided by `Tweak`s. These are small plugins that
|
||||
implement the [Tweak] interface. They live in the [refactor/tweaks] directory
|
||||
and are registered through the linker. Given a selection, they can (quickly)
|
||||
determine whether they apply there and (maybe slowly) generate the actual edits.
|
||||
The LSP code-actions flow is built out of these primitives.
|
||||
|
||||
## Feature infrastructure
|
||||
|
||||
### Parsing and ASTs
|
||||
|
||||
The representation of a parsed file in clangd is [ParsedAST].
|
||||
As the name suggests this is mostly used to access Clang's AST
|
||||
(`clang::ASTContext`), but extends it by:
|
||||
|
||||
- recording and exposing information gathered from callbacks (e.g. diagnostics)
|
||||
- encapsulating the other objects (e.g. SourceManager and Preprocessor) and
|
||||
keeps them alive with the correct lifetime
|
||||
|
||||
`ParsedAST::build()` is where we run the clang parser.
|
||||
Some low-level bits (creating `CompilerInstance`) are in [Compiler.h]
|
||||
instead, and are reused when we run clang without retaining an AST (code
|
||||
completion, indexing, preambles).
|
||||
|
||||
The [PreambleData] structure similarly extends Clang's `PrecompiledPreamble`
|
||||
class with extra recorded information. It contains the AST of included headers
|
||||
and is only rebuilt when those headers change. The preamble is large, it's kept
|
||||
on disk by default and parts are deserialized on demand.
|
||||
|
||||
### Abstractions over clang AST
|
||||
|
||||
Several tasks come up in various features and we have reusable solutions:
|
||||
|
||||
- [SelectionTree] identifies the AST nodes corresponding to a point or range
|
||||
in the source code.
|
||||
Used in go-to-definition, code actions, and many other features.
|
||||
- [targetDecl()] identifies the declaration an AST node refers to.
|
||||
Used e.g. in go-to-definition.
|
||||
- [findExplicitReferences()] traverses a chunk of AST and lists declarations
|
||||
referenced. Used e.g. in find-references and rename. Should be used for
|
||||
indexing, one day.
|
||||
|
||||
### Index
|
||||
|
||||
Operations that need information outside the current file/AST make use of
|
||||
[the clangd index], which is in the [index/] directory.
|
||||
|
||||
[SymbolIndex] is the index interface exposed to consuming features, and
|
||||
describes the data/queries they should provide. (`Symbol`, `Ref`, etc).
|
||||
It has several implementations used as building-blocks:
|
||||
|
||||
- [MemIndex] is a simple in-memory implementation that's cheap to construct.
|
||||
- [Dex] is a more complex one with a scalable fuzzyFind search.
|
||||
- [MergedIndex] combines indexes, merging results.
|
||||
- [IndexClient] is a client for a remote index service over grpc.
|
||||
|
||||
[SymbolCollector] extracts indexable data from a translation unit.
|
||||
[index/Serialization.h] defines a binary format to store/load index data.
|
||||
|
||||
These building blocks are used to provide clangd's index data:
|
||||
|
||||
- [BackgroundIndex] runs `SymbolCollector` over project files in background
|
||||
threads, periodically combining the results into an exposed `Dex` index.
|
||||
Index data is also written to disk and only reindexed when these are stale.
|
||||
- [FileIndex] stores the index information from all opened files and their
|
||||
preambles, running SymbolCollector on ASTs after they are rebuilt. It is a
|
||||
`MergedIndex` of a `Dex` of the preambles and a `MemIndex` of the main-file
|
||||
symbols. This is also known as the "dynamic index".
|
||||
- The "static index" is configured in `main` and may be a
|
||||
simple index file (generated by [indexer/IndexerMain.cpp]) loaded into `Dex`,
|
||||
a `RemoteIndex`, or nothing at all.
|
||||
|
||||
## Dependencies
|
||||
|
||||
### Clang libraries
|
||||
|
||||
Clang code structure is a huge topic, but the most important pieces for clangd:
|
||||
|
||||
- AST: [clang/AST/] defines the data structures that represent
|
||||
parsed C++ code, such the [Decl], [Stmt], and [Type] hierarchies.
|
||||
[RecursiveASTVisitor] is the generic mechanism to walk an AST.
|
||||
- Preambles: [PrecompiledPreamble] wraps a serialized partial AST that can be
|
||||
lazy-loaded from disk, clangd relies *heavily* on this optimization.
|
||||
- Preprocessor: at the token level, the [Preprocessor] handles directives and
|
||||
macro expansion, and we use [PPCallbacks] hooks to listen for events.
|
||||
|
||||
### Clang-tools libraries
|
||||
|
||||
clangd shares code with other tools derived from the Clang compiler, these
|
||||
libraries live outside clangd.
|
||||
|
||||
- [syntax::TokenBuffer] captures token-boundary and preprocessing information
|
||||
that clang itself doesn't preserve.
|
||||
- [clang/Index/] implements an indexing-oriented traversal of Clang ASTs, which
|
||||
is used in clangd's index.
|
||||
- [clang/Format/] is the clang-format logic used to satisfy formatting requests
|
||||
and also to format newly-inserted code.
|
||||
- [tooling::CompilationDatabase] is the foundation for clangd's integration
|
||||
with build systems.
|
||||
|
||||
### General support libraries
|
||||
|
||||
Like most LLVM code, clangd heavily uses [llvm/ADT/] and [llvm/Support/] to
|
||||
supplement the standard library. We try to avoid other LLVM dependencies.
|
||||
|
||||
clangd has its own [support/] library, conceptually similar to `llvm/Support`.
|
||||
It contains libraries that are general-purpose, but not a good fit for llvm as a
|
||||
whole (too opinionated, or focused on multithreading). The most prominent:
|
||||
|
||||
- [ThreadsafeFS] addresses the problems with llvm's FileSystem abstraction for
|
||||
multithreaded programs.
|
||||
- [Context] is used to passing certain "ambient" data around within the current
|
||||
thread, and automatically propagating it when scheduling on another thread.
|
||||
(It is related to dynamically-scoped variables, and thread-local storage).
|
||||
It's used for certain settings like overriding LSP encoding, for tracking
|
||||
actions across threads, request cancellation and more.
|
||||
- [support/Logger.h] provides a concise, threadsafe logging API and lets
|
||||
embedders handle logs.
|
||||
- [support/Trace.h] allows instrumentation of clangd's implementation with
|
||||
events and metrics for performance analysis etc.
|
||||
|
||||
## Testing
|
||||
|
||||
Most of the tests are in the [unittests/] directory (despite being a mix of unit
|
||||
and integration tests). Test files are mostly named after the file they're
|
||||
testing, and use the [googletest] framework.
|
||||
|
||||
Some helpers are widely shared between tests:
|
||||
|
||||
- [TestTU] lets tests tersely specify code for a test case, and can prepare
|
||||
`ParsedAST` and other structures needed for testing features on that code.
|
||||
- [Annotations] recognizes code examples with marked points and ranges.
|
||||
This is used e.g. to specify tests for "go to definition".
|
||||
|
||||
clangd has a small number of black-box tests in [test/]. These use LLVM [lit]
|
||||
and [FileCheck] to drive the clangd binary and verify output. They smoke-test
|
||||
clangd as an LSP server, and test a few hard-to-isolate features.
|
||||
|
||||
[FileCheck]: https://llvm.org/docs/CommandGuide/FileCheck.html
|
||||
[googletest]: https://github.com/google/googletest
|
||||
[lit]: https://llvm.org/docs/CommandGuide/lit.html
|
||||
[llvm-project]: https://github.com/llvm/llvm-project
|
||||
[woboq]: https://code.woboq.org/llvm/clang-tools-extra/clangd/
|
||||
|
||||
[threads and request handling]: threads
|
||||
[the clangd index]: index
|
||||
|
||||
[clangd]: https://code.woboq.org/llvm/clang-tools-extra/clangd/
|
||||
[index/]: https://code.woboq.org/llvm/clang-tools-extra/clangd/index/
|
||||
[test/]: https://code.woboq.org/llvm/clang-tools-extra/clangd/test/
|
||||
[refactor/tweaks/]: https://code.woboq.org/llvm/clang-tools-extra/clangd/refactor/tweaks/
|
||||
[support/]: https://code.woboq.org/llvm/clang-tools-extra/clangd/support/
|
||||
[unittests/]: https://code.woboq.org/llvm/clang-tools-extra/clangd/unittests/
|
||||
[xpc/]: https://code.woboq.org/llvm/clang-tools-extra/clangd/xpc/
|
||||
|
||||
[llvm/ADT/]: https://code.woboq.org/llvm/llvm/include/llvm/ADT/
|
||||
[llvm/Support/]: https://code.woboq.org/llvm/llvm/include/llvm/Support/
|
||||
[clang/AST/]: https://code.woboq.org/llvm/clang/include/clang/AST/
|
||||
[clang/Index/]: https://code.woboq.org/llvm/clang/include/clang/Index/
|
||||
[clang/Format/]: https://code.woboq.org/llvm/clang/include/clang/Format/
|
||||
|
||||
[CodeComplete.h]: https://code.woboq.org/llvm/clang-tools-extra/clangd/CodeComplete.h.html
|
||||
[Compiler.h]: https://code.woboq.org/llvm/clang-tools-extra/clangd/Compiler.h.html
|
||||
[Protocol.h]:https://code.woboq.org/llvm/clang-tools-extra/clangd/Protocol.h.html
|
||||
[Quality.h]: https://code.woboq.org/llvm/clang-tools-extra/clangd/Quality.h.html
|
||||
[index/Serialization.h]: https://code.woboq.org/llvm/clang-tools-extra/clangd/index/Serialization.h.html
|
||||
[indexer/IndexerMain.cpp]: https://code.woboq.org/llvm/clang-tools-extra/clangd/indexer/IndexerMain.h.html
|
||||
[support/Logger.h]: https://code.woboq.org/llvm/clang-tools-extra/clangd/support/Logger.h.html
|
||||
[support/Trace.h]: https://code.woboq.org/llvm/clang-tools-extra/clangd/support/Trace.h.html
|
||||
|
||||
[SemaCodeComplete.cpp]: https://code.woboq.org/llvm/clang/lib/Sema/SemaCodeComplete.cpp.html
|
||||
|
||||
[Annotations]: https://code.woboq.org/llvm/clang-tools-extra/clangd/unittests/Annotations.h.html#clang::clangd::Annotations
|
||||
[BackgroundIndex]: https://code.woboq.org/llvm/clang-tools-extra/clangd/index/Background.h.html#clang::clangd::BackgroundIndex
|
||||
[ClangdLSPServer]: https://code.woboq.org/llvm/clang-tools-extra/clangd/ClangdLSPServer.h.html#clang::clangd::ClangdLSPServer
|
||||
[ClangdServer]: https://code.woboq.org/llvm/clang-tools-extra/clangd/ClangdServer.h.html#clang::clangd::ClangdServer
|
||||
[CommandMangler]: https://code.woboq.org/llvm/clang-tools-extra/clangd/CompileCommands.h.html#clang::clangd::CommandMangler
|
||||
[Context]: https://code.woboq.org/llvm/clang-tools-extra/clangd/Context.h.html#clang::clangd::Context
|
||||
[Dex]: https://code.woboq.org/llvm/clang-tools-extra/clangd/index/dex/Dex.h.html#clang::clangd::dex::Dex
|
||||
[Diag]: https://code.woboq.org/llvm/clang-tools-extra/clangd/Diagnostics.h.html#clang::clangd::Diag
|
||||
[FileIndex]: https://code.woboq.org/llvm/clang-tools-extra/clangd/index/FileIndex.h.html#clang::clangd::FileIndex
|
||||
[FuzzyMatcher]: https://code.woboq.org/llvm/clang-tools-extra/clangd/FuzzyMatch.h.html#clang::clangd::FuzzyMatcher
|
||||
[GlobalCompilationDatabase]: https://code.woboq.org/llvm/clang-tools-extra/clangd/GlobalCompilationDatabase.h.html#clang::clangd::GlobalCompilationDatabase
|
||||
[IncludeFixer]: https://code.woboq.org/llvm/clang-tools-extra/clangd/IncludeFixer.h.html#clang::clangd::IncludeFixer
|
||||
[IndexClient]: https://code.woboq.org/llvm/clang-tools-extra/clangd/index/remote/Client.cpp.html#clang::clangd::remote::(anonymousnamespace)::IndexClient
|
||||
[JSONTransport]: https://code.woboq.org/llvm/clang-tools-extra/clangd/JSONTransport.cpp.html#clang::clangd::(anonymousnamespace)::JSONTransport
|
||||
[MemIndex]: https://code.woboq.org/llvm/clang-tools-extra/clangd/index/MemIndex.h.html#clang::clangd::MemIndex
|
||||
[MergedIndex]: https://code.woboq.org/llvm/clang-tools-extra/clangd/index/Merge.h.html#clang::clangd::MergedIndex
|
||||
[ParsedAST]: https://code.woboq.org/llvm/clang-tools-extra/clangd/ParsedAST.h.html#clang::clangd::ParsedAST
|
||||
[ParsingCallbacks]: https://code.woboq.org/llvm/clang-tools-extra/clangd/TUScheduler.h.html#clang::clangd::ParsingCallbacks
|
||||
[PreambleData]: https://code.woboq.org/llvm/clang-tools-extra/clangd/Preamble.h.html#clang::clangd::PreambleData
|
||||
[SelectionTree]: https://code.woboq.org/llvm/clang-tools-extra/clangd/Selection.h.html#clang::clangd::SelectionTree
|
||||
[StoreDiags]: https://code.woboq.org/llvm/clang-tools-extra/clangd/Diagnostics.h.html#clang::clangd::StoreDiags
|
||||
[SymbolCollector]: https://code.woboq.org/llvm/clang-tools-extra/clangd/index/SymbolCollector.h.html#clang::clangd::SymbolCollector
|
||||
[SymbolIndex]: https://code.woboq.org/llvm/clang-tools-extra/clang-include-fixer/SymbolIndex.h.html#clang::include_fixer::SymbolIndex
|
||||
[TUScheduler]: https://code.woboq.org/llvm/clang-tools-extra/clangd/TUScheduler.h.html#clang::clangd::TUScheduler
|
||||
[TestTU]: https://code.woboq.org/llvm/clang-tools-extra/clangd/unittests/TestTU.h.html#clang::clangd::TestTU
|
||||
[ThreadsafeFS]: https://code.woboq.org/llvm/clang-tools-extra/clangd/support/ThreadsafeFS.h.html#clang::clangd::ThreadsafeFS
|
||||
[Transport]: https://code.woboq.org/llvm/clang-tools-extra/clangd/Transport.h.html#clang::clangd::Transport
|
||||
[Tweak]: https://code.woboq.org/llvm/clang-tools-extra/clangd/refactor/Tweak.h.html#clang::clangd::Tweak
|
||||
[findExplicitReferences()]: https://code.woboq.org/llvm/clang-tools-extra/clangd/FindTarget.h.html
|
||||
[getHover()]: https://code.woboq.org/llvm/clang-tools-extra/clangd/Hover.h.html
|
||||
[locateSymbolAt()]: https://code.woboq.org/llvm/clang-tools-extra/clangd/XRefs.h.html
|
||||
[main()]: https://code.woboq.org/llvm/clang-tools-extra/clangd/tool/ClangdMain.cpp.html#main
|
||||
[targetDecl()]: https://code.woboq.org/llvm/clang-tools-extra/clangd/FindTarget.h.html
|
||||
|
||||
[Decl]: https://code.woboq.org/llvm/clang/include/clang/AST/DeclBase.h.html#clang::Decl
|
||||
[Stmt]: https://code.woboq.org/llvm/clang/include/clang/AST/Stmt.h.html#clang::Stmt
|
||||
[Type]: https://code.woboq.org/llvm/clang/include/clang/AST/Type.h.html#clang::Type
|
||||
[Preprocessor]: https://code.woboq.org/llvm/clang/include/clang/Lex/Preprocessor.h.html#clang::Preprocessor
|
||||
[PPCallbacks]: https://code.woboq.org/llvm/clang/include/clang/Lex/PPCallbacks.h.html#clang::PPCallbacks
|
||||
[RecursiveASTVisitor]: https://code.woboq.org/llvm/clang/include/clang/AST/RecursiveASTVisitor.h.html#clang::RecursiveASTVisitor
|
||||
[PrecompiledPreamble]: https://code.woboq.org/llvm/clang/include/clang/Frontend/PrecompiledPreamble.h.html#clang::PrecompiledPreamble
|
||||
[syntax::TokenBuffer]: https://code.woboq.org/llvm/clang/include/clang/Tooling/Syntax/Tokens.h.html#clang::syntax::TokenBuffer
|
||||
[tooling::CompilationDatabase]: https://code.woboq.org/llvm/clang/include/clang/Tooling/CompilationDatabase.h.html#clang::tooling::CompilationDatabase
|
Loading…
Reference in New Issue
Block a user