]> git.lizzy.rs Git - rust.git/blob - ARCHITECTURE.md
Merge #885
[rust.git] / ARCHITECTURE.md
1 # Architecture
2
3 This document describes the high-level architecture of rust-analyzer.
4 If you want to familiarize yourself with the code base, you are just
5 in the right place!
6
7 See also the [guide](./guide.md), which walks through a particular snapshot of
8 rust-analyzer code base.
9
10 ## The Big Picture
11
12 ![](https://user-images.githubusercontent.com/1711539/50114578-e8a34280-0255-11e9-902c-7cfc70747966.png)
13
14 On the highest level, rust-analyzer is a thing which accepts input source code
15 from the client and produces a structured semantic model of the code.
16
17 More specifically, input data consists of a set of test files (`(PathBuf,
18 String)` pairs) and information about project structure, captured in the so called
19 `CrateGraph`. The crate graph specifies which files are crate roots, which cfg
20 flags are specified for each crate (TODO: actually implement this) and what
21 dependencies exist between the crates. The analyzer keeps all this input data in
22 memory and never does any IO. Because the input data is source code, which
23 typically measures in tens of megabytes at most, keeping all input data in
24 memory is OK.
25
26 A "structured semantic model" is basically an object-oriented representation of
27 modules, functions and types which appear in the source code. This representation
28 is fully "resolved": all expressions have types, all references are bound to
29 declarations, etc.
30
31 The client can submit a small delta of input data (typically, a change to a
32 single file) and get a fresh code model which accounts for changes.
33
34 The underlying engine makes sure that model is computed lazily (on-demand) and
35 can be quickly updated for small modifications.
36
37
38 ## Code generation
39
40 Some of the components of this repository are generated through automatic
41 processes. These are outlined below:
42
43 - `gen-syntax`: The kinds of tokens that are reused in several places, so a generator
44   is used. We use tera templates to generate the files listed below, based on
45   the grammar described in [grammar.ron]:
46   - [ast/generated.rs][ast generated] in `ra_syntax` based on
47     [ast/generated.tera.rs][ast source]
48   - [syntax_kinds/generated.rs][syntax_kinds generated] in `ra_syntax` based on
49     [syntax_kinds/generated.tera.rs][syntax_kinds source]
50
51 [tera]: https://tera.netlify.com/
52 [grammar.ron]: ./crates/ra_syntax/src/grammar.ron
53 [ast generated]: ./crates/ra_syntax/src/ast/generated.rs
54 [ast source]: ./crates/ra_syntax/src/ast/generated.rs.tera
55 [syntax_kinds generated]: ./crates/ra_syntax/src/syntax_kinds/generated.rs
56 [syntax_kinds source]: ./crates/ra_syntax/src/syntax_kinds/generated.rs.tera
57
58
59 ## Code Walk-Through
60
61 ### `crates/ra_syntax`
62
63 Rust syntax tree structure and parser. See
64 [RFC](https://github.com/rust-lang/rfcs/pull/2256) for some design notes.
65
66 - [rowan](https://github.com/rust-analyzer/rowan) library is used for constructing syntax trees.
67 - `grammar` module is the actual parser. It is a hand-written recursive descent parser, which
68   produces a sequence of events like "start node X", "finish not Y". It works similarly to [kotlin's parser](https://github.com/JetBrains/kotlin/blob/4d951de616b20feca92f3e9cc9679b2de9e65195/compiler/frontend/src/org/jetbrains/kotlin/parsing/KotlinParsing.java),
69   which is a good source of inspiration for dealing with syntax errors and incomplete input. Original [libsyntax parser](https://github.com/rust-lang/rust/blob/6b99adeb11313197f409b4f7c4083c2ceca8a4fe/src/libsyntax/parse/parser.rs)
70   is what we use for the definition of the Rust language.
71 - `parser_api/parser_impl` bridges the tree-agnostic parser from `grammar` with `rowan` trees.
72   This is the thing that turns a flat list of events into a tree (see `EventProcessor`)
73 - `ast` provides a type safe API on top of the raw `rowan` tree.
74 - `grammar.ron` RON description of the grammar, which is used to
75   generate `syntax_kinds` and `ast` modules, using `cargo gen-syntax` command.
76 - `algo`: generic tree algorithms, including `walk` for O(1) stack
77   space tree traversal (this is cool) and `visit` for type-driven
78   visiting the nodes (this is double plus cool, if you understand how
79   `Visitor` works, you understand the design of syntax trees).
80
81 Tests for ra_syntax are mostly data-driven: `tests/data/parser` contains a bunch of `.rs`
82 (test vectors) and `.txt` files with corresponding syntax trees. During testing, we check
83 `.rs` against `.txt`. If the `.txt` file is missing, it is created (this is how you update
84 tests). Additionally, running `cargo gen-tests` will walk the grammar module and collect
85 all `//test test_name` comments into files inside `tests/data` directory.
86
87 See [#93](https://github.com/rust-analyzer/rust-analyzer/pull/93) for an example PR which
88 fixes a bug in the grammar.
89
90 ### `crates/ra_db`
91
92 We use the [salsa](https://github.com/salsa-rs/salsa) crate for incremental and
93 on-demand computation. Roughly, you can think of salsa as a key-value store, but
94 it also can compute derived values using specified functions. The `ra_db` crate
95 provides basic infrastructure for interacting with salsa. Crucially, it
96 defines most of the "input" queries: facts supplied by the client of the
97 analyzer. Reading the docs of the `ra_db::input` module should be useful:
98 everything else is strictly derived from those inputs.
99
100 ### `crates/ra_hir`
101
102 HIR provides high-level "object oriented" access to Rust code.
103
104 The principal difference between HIR and syntax trees is that HIR is bound to a
105 particular crate instance. That is, it has cfg flags and features applied (in
106 theory, in practice this is to be implemented). So, the relation between
107 syntax and HIR is many-to-one. The `source_binder` module is responsible for
108 guessing a HIR for a particular source position.
109
110 Underneath, HIR works on top of salsa, using a `HirDatabase` trait.
111
112 ### `crates/ra_ide_api`
113
114 A stateful library for analyzing many Rust files as they change. `AnalysisHost`
115 is a mutable entity (clojure's atom) which holds the current state, incorporates
116 changes and hands out `Analysis` --- an immutable and consistent snapshot of
117 the world state at a point in time, which actually powers analysis.
118
119 One interesting aspect of analysis is its support for cancellation. When a
120 change is applied to `AnalysisHost`, first all currently active snapshots are
121 canceled. Only after all snapshots are dropped the change actually affects the
122 database.
123
124 APIs in this crate are IDE centric: they take text offsets as input and produce
125 offsets and strings as output. This works on top of rich code model powered by
126 `hir`.
127
128 ### `crates/ra_ide_api_light`
129
130 All IDE features which can be implemented if you only have access to a single
131 file. `ra_ide_api_light` could be used to enhance editing of Rust code without
132 the need to fiddle with build-systems, file synchronization and such.
133
134 In a sense, `ra_ide_api_light` is just a bunch of pure functions which take a
135 syntax tree as input.
136
137 The tests for `ra_ide_api_light` are `#[cfg(test)] mod tests` unit-tests spread
138 throughout its modules.
139
140
141 ### `crates/ra_lsp_server`
142
143 An LSP implementation which wraps `ra_ide_api` into a langauge server protocol.
144
145 ### `crates/ra_vfs`
146
147 Although `hir` and `ra_ide_api` don't do any IO, we need to be able to read
148 files from disk at the end of the day. This is what `ra_vfs` does. It also
149 manages overlays: "dirty" files in the editor, whose "true" contents is
150 different from data on disk.
151
152 ### `crates/gen_lsp_server`
153
154 A language server scaffold, exposing a synchronous crossbeam-channel based API.
155 This crate handles protocol handshaking and parsing messages, while you
156 control the message dispatch loop yourself.
157
158 Run with `RUST_LOG=sync_lsp_server=debug` to see all the messages.
159
160 ### `crates/ra_cli`
161
162 A CLI interface to rust-analyzer.
163
164 ### `crate/tools`
165
166 Custom Cargo tasks used to develop rust-analyzer:
167
168 - `cargo gen-syntax` -- generate `ast` and `syntax_kinds`
169 - `cargo gen-tests` -- collect inline tests from grammar
170 - `cargo install-code` -- build and install VS Code extension and server
171
172 ### `editors/code`
173
174 VS Code plugin
175
176
177 ## Common workflows
178
179 To try out VS Code extensions, run `cargo install-code`.  This installs both the
180 `ra_lsp_server` binary and the VS Code extension. To install only the binary, use
181 `cargo install-lsp` (shorthand for `cargo install --path crates/ra_lsp_server --force`)
182
183 To see logs from the language server, set `RUST_LOG=info` env variable. To see
184 all communication between the server and the client, use
185 `RUST_LOG=gen_lsp_server=debug` (this will print quite a bit of stuff).
186
187 There's `rust-analyzer: status` command which prints common high-level debug
188 info. In particular, it prints info about memory usage of various data
189 structures, and, if compiled with jemalloc support (`cargo jinstall-lsp` or 
190 `cargo install --path crates/ra_lsp_server --force --features jemalloc`), includes
191  statistic about the heap.
192
193 To run tests, just `cargo test`.
194
195 To work on the VS Code extension, launch code inside `editors/code` and use `F5` to
196 launch/debug. To automatically apply formatter and linter suggestions, use `npm
197 run fix`.