mirror of
				https://github.com/rust-lang/rust-analyzer.git
				synced 2025-11-03 13:13:18 +00:00 
			
		
		
		
	update docs/dev/guide.md based on 2024-01-01 release
This commit is contained in:
		
							parent
							
								
									52f7575c35
								
							
						
					
					
						commit
						f098123122
					
				@ -2,13 +2,15 @@
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
## About the guide
 | 
					## About the guide
 | 
				
			||||||
 | 
					
 | 
				
			||||||
This guide describes the current state of rust-analyzer as of 2019-01-20 (git
 | 
					This guide describes the current state of rust-analyzer as of the 2024-01-01 release
 | 
				
			||||||
tag [guide-2019-01]). Its purpose is to document various problems and
 | 
					(git tag [2024-01-01]). Its purpose is to document various problems and
 | 
				
			||||||
architectural solutions related to the problem of building IDE-first compiler
 | 
					architectural solutions related to the problem of building IDE-first compiler
 | 
				
			||||||
for Rust. There is a video version of this guide as well:
 | 
					for Rust. There is a video version of this guide as well -
 | 
				
			||||||
 | 
					however, it's based on an older 2019-01-20 release (git tag [guide-2019-01]):
 | 
				
			||||||
https://youtu.be/ANKBNiSWyfc.
 | 
					https://youtu.be/ANKBNiSWyfc.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
[guide-2019-01]: https://github.com/rust-lang/rust-analyzer/tree/guide-2019-01
 | 
					[guide-2019-01]: https://github.com/rust-lang/rust-analyzer/tree/guide-2019-01
 | 
				
			||||||
 | 
					[2024-01-01]: https://github.com/rust-lang/rust-analyzer/tree/2024-01-01
 | 
				
			||||||
 | 
					
 | 
				
			||||||
## The big picture
 | 
					## The big picture
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -40,8 +42,8 @@ terms of files and offsets, and **not** in terms of Rust concepts like structs,
 | 
				
			|||||||
traits, etc. The "typed" API with Rust specific types is slightly lower in the
 | 
					traits, etc. The "typed" API with Rust specific types is slightly lower in the
 | 
				
			||||||
stack, we'll talk about it later.
 | 
					stack, we'll talk about it later.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
[`AnalysisHost`]: https://github.com/rust-lang/rust-analyzer/blob/guide-2019-01/crates/ra_ide_api/src/lib.rs#L265-L284
 | 
					[`AnalysisHost`]: https://github.com/rust-lang/rust-analyzer/blob/2024-01-01/crates/ide/src/lib.rs#L161-L213
 | 
				
			||||||
[`Analysis`]: https://github.com/rust-lang/rust-analyzer/blob/guide-2019-01/crates/ra_ide_api/src/lib.rs#L291-L478
 | 
					[`Analysis`]: https://github.com/rust-lang/rust-analyzer/blob/2024-01-01/crates/ide/src/lib.rs#L220-L761
 | 
				
			||||||
 | 
					
 | 
				
			||||||
The reason for this separation of `Analysis` and `AnalysisHost` is that we want to apply
 | 
					The reason for this separation of `Analysis` and `AnalysisHost` is that we want to apply
 | 
				
			||||||
changes "uniquely", but we might also want to fork an `Analysis` and send it to
 | 
					changes "uniquely", but we might also want to fork an `Analysis` and send it to
 | 
				
			||||||
@ -65,18 +67,23 @@ Next, let's talk about what the inputs to the `Analysis` are, precisely.
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
rust-analyzer never does any I/O itself, all inputs get passed explicitly via
 | 
					rust-analyzer never does any I/O itself, all inputs get passed explicitly via
 | 
				
			||||||
the `AnalysisHost::apply_change` method, which accepts a single argument, a
 | 
					the `AnalysisHost::apply_change` method, which accepts a single argument, a
 | 
				
			||||||
`AnalysisChange`. [`AnalysisChange`] is a builder for a single change
 | 
					`Change`. [`Change`] is a wrapper for `FileChange` that adds proc-macro knowledge.
 | 
				
			||||||
"transaction", so it suffices to study its methods to understand all of the
 | 
					[`FileChange`] is a builder for a single change "transaction", so it suffices
 | 
				
			||||||
input data.
 | 
					to study its methods to understand all the input data.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
[`AnalysisChange`]: https://github.com/rust-lang/rust-analyzer/blob/guide-2019-01/crates/ra_ide_api/src/lib.rs#L119-L167
 | 
					[`Change`]: https://github.com/rust-lang/rust-analyzer/blob/2024-01-01/crates/hir-expand/src/change.rs#L10-L42
 | 
				
			||||||
 | 
					[`FileChange`]: https://github.com/rust-lang/rust-analyzer/blob/2024-01-01/crates/base-db/src/change.rs#L14-L78
 | 
				
			||||||
 | 
					
 | 
				
			||||||
The `(add|change|remove)_file` methods control the set of the input files, where
 | 
					The `change_file` methods controls the set of the input files, where each file
 | 
				
			||||||
each file has an integer id (`FileId`, picked by the client), text (`String`)
 | 
					has an integer id (`FileId`, picked by the client), text (`Option<Arc<str>>`).
 | 
				
			||||||
and a filesystem path. Paths are tricky; they'll be explained below, in source roots
 | 
					Paths are tricky; they'll be explained below, in source roots section, 
 | 
				
			||||||
section, together with the `add_root` method. The `add_library` method allows us to add a
 | 
					together with the `set_roots` method. The "source root" [`is_library`] flag
 | 
				
			||||||
group of files which are assumed to rarely change. It's mostly an optimization
 | 
					along with the concept of [`durability`] allows us to add a group of files which 
 | 
				
			||||||
and does not change the fundamental picture.
 | 
					are assumed to rarely change. It's mostly an optimization and does not change 
 | 
				
			||||||
 | 
					the fundamental picture.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[`is_library`]: https://github.com/rust-lang/rust-analyzer/blob/2024-01-01/crates/base-db/src/input.rs#L38
 | 
				
			||||||
 | 
					[`durability`]: https://github.com/rust-lang/rust-analyzer/blob/2024-01-01/crates/base-db/src/change.rs#L80-L86
 | 
				
			||||||
 | 
					
 | 
				
			||||||
The `set_crate_graph` method allows us to control how the input files are partitioned
 | 
					The `set_crate_graph` method allows us to control how the input files are partitioned
 | 
				
			||||||
into compilation units -- crates. It also controls (in theory, not implemented
 | 
					into compilation units -- crates. It also controls (in theory, not implemented
 | 
				
			||||||
@ -134,49 +141,53 @@ the source root, even `/dev/random`.
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
## Language Server Protocol
 | 
					## Language Server Protocol
 | 
				
			||||||
 | 
					
 | 
				
			||||||
Now let's see how the `Analysis` API is exposed via the JSON RPC based language server protocol. The
 | 
					Now let's see how the `Analysis` API is exposed via the JSON RPC based language server protocol. 
 | 
				
			||||||
hard part here is managing changes (which can come either from the file system
 | 
					The hard part here is managing changes (which can come either from the file system
 | 
				
			||||||
or from the editor) and concurrency (we want to spawn background jobs for things
 | 
					or from the editor) and concurrency (we want to spawn background jobs for things
 | 
				
			||||||
like syntax highlighting). We use the event loop pattern to manage the zoo, and
 | 
					like syntax highlighting). We use the event loop pattern to manage the zoo, and
 | 
				
			||||||
the loop is the [`main_loop_inner`] function. The [`main_loop`] does a one-time
 | 
					the loop is the [`GlobalState::run`] function initiated by [`main_loop`] after
 | 
				
			||||||
initialization and tearing down of the resources.
 | 
					[`GlobalState::new`] does a one-time initialization and tearing down of the resources.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
[`main_loop`]: https://github.com/rust-lang/rust-analyzer/blob/guide-2019-01/crates/ra_lsp_server/src/main_loop.rs#L51-L110
 | 
					[`main_loop`]: https://github.com/rust-lang/rust-analyzer/blob/2024-01-01/crates/rust-analyzer/src/main_loop.rs#L31-L54
 | 
				
			||||||
[`main_loop_inner`]: https://github.com/rust-lang/rust-analyzer/blob/guide-2019-01/crates/ra_lsp_server/src/main_loop.rs#L156-L258
 | 
					[`GlobalState::new`]: https://github.com/rust-lang/rust-analyzer/blob/2024-01-01/crates/rust-analyzer/src/global_state.rs#L148-L215
 | 
				
			||||||
 | 
					[`GlobalState::run`]: https://github.com/rust-lang/rust-analyzer/blob/2024-01-01/crates/rust-analyzer/src/main_loop.rs#L114-L140
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
Let's walk through a typical analyzer session!
 | 
					Let's walk through a typical analyzer session!
 | 
				
			||||||
 | 
					
 | 
				
			||||||
First, we need to figure out what to analyze. To do this, we run `cargo
 | 
					First, we need to figure out what to analyze. To do this, we run `cargo
 | 
				
			||||||
metadata` to learn about Cargo packages for current workspace and dependencies,
 | 
					metadata` to learn about Cargo packages for current workspace and dependencies,
 | 
				
			||||||
and we run `rustc --print sysroot` and scan the "sysroot" (the directory containing the current Rust toolchain's files) to learn about crates like
 | 
					and we run `rustc --print sysroot` and scan the "sysroot"
 | 
				
			||||||
`std`. Currently we load this configuration once at the start of the server, but
 | 
					(the directory containing the current Rust toolchain's files) to learn about crates 
 | 
				
			||||||
it should be possible to dynamically reconfigure it later without restart.
 | 
					like `std`. This happens in the [`GlobalState::fetch_workspaces`] method.
 | 
				
			||||||
 | 
					We load this configuration at the start of the server in [`GlobalState::new`],
 | 
				
			||||||
 | 
					but it's also triggered by workspace change events and requests to reload the
 | 
				
			||||||
 | 
					workspace from the client.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
[main_loop.rs#L62-L70](https://github.com/rust-lang/rust-analyzer/blob/guide-2019-01/crates/ra_lsp_server/src/main_loop.rs#L62-L70)
 | 
					[`GlobalState::fetch_workspaces`]: https://github.com/rust-lang/rust-analyzer/blob/2024-01-01/crates/rust-analyzer/src/reload.rs#L186-L257
 | 
				
			||||||
 | 
					
 | 
				
			||||||
The [`ProjectModel`] we get after this step is very Cargo and sysroot specific,
 | 
					The [`ProjectModel`] we get after this step is very Cargo and sysroot specific,
 | 
				
			||||||
it needs to be lowered to get the input in the form of `AnalysisChange`. This
 | 
					it needs to be lowered to get the input in the form of `Change`. This happens
 | 
				
			||||||
happens in [`ServerWorldState::new`] method. Specifically
 | 
					in [`GlobalState::process_changes`] method. Specifically
 | 
				
			||||||
 | 
					
 | 
				
			||||||
* Create a `SourceRoot` for each Cargo package and sysroot.
 | 
					* Create `SourceRoot`s for each Cargo package(s) and sysroot.
 | 
				
			||||||
* Schedule a filesystem scan of the roots.
 | 
					* Schedule a filesystem scan of the roots.
 | 
				
			||||||
* Create an analyzer's `Crate` for each Cargo **target** and sysroot crate.
 | 
					* Create an analyzer's `Crate` for each Cargo **target** and sysroot crate.
 | 
				
			||||||
* Setup dependencies between the crates.
 | 
					* Setup dependencies between the crates.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
[`ProjectModel`]: https://github.com/rust-lang/rust-analyzer/blob/guide-2019-01/crates/ra_lsp_server/src/project_model.rs#L16-L20
 | 
					[`ProjectModel`]: https://github.com/rust-lang/rust-analyzer/blob/2024-01-01/crates/project-model/src/workspace.rs#L57-L100
 | 
				
			||||||
[`ServerWorldState::new`]: https://github.com/rust-lang/rust-analyzer/blob/guide-2019-01/crates/ra_lsp_server/src/server_world.rs#L38-L160
 | 
					[`GlobalState::process_changes`]: https://github.com/rust-lang/rust-analyzer/blob/2024-01-01/crates/rust-analyzer/src/global_state.rs#L217-L356
 | 
				
			||||||
 | 
					
 | 
				
			||||||
The results of the scan (which may take a while) will be processed in the body
 | 
					The results of the scan (which may take a while) will be processed in the body
 | 
				
			||||||
of the main loop, just like any other change. Here's where we handle:
 | 
					of the main loop, just like any other change. Here's where we handle:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
* [File system changes](https://github.com/rust-lang/rust-analyzer/blob/guide-2019-01/crates/ra_lsp_server/src/main_loop.rs#L194)
 | 
					* [File system changes](https://github.com/rust-lang/rust-analyzer/blob/2024-01-01/crates/rust-analyzer/src/main_loop.rs#L273)
 | 
				
			||||||
* [Changes from the editor](https://github.com/rust-lang/rust-analyzer/blob/guide-2019-01/crates/ra_lsp_server/src/main_loop.rs#L377)
 | 
					* [Changes from the editor](https://github.com/rust-lang/rust-analyzer/blob/2024-01-01/crates/rust-analyzer/src/main_loop.rs#L801-L803)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
After a single loop's turn, we group the changes into one `AnalysisChange` and
 | 
					After a single loop's turn, we group the changes into one `Change` and
 | 
				
			||||||
[apply] it. This always happens on the main thread and blocks the loop.
 | 
					[apply] it. This always happens on the main thread and blocks the loop.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
[apply]: https://github.com/rust-lang/rust-analyzer/blob/guide-2019-01/crates/ra_lsp_server/src/server_world.rs#L216
 | 
					[apply]: https://github.com/rust-lang/rust-analyzer/blob/2024-01-01/crates/rust-analyzer/src/global_state.rs#L333
 | 
				
			||||||
 | 
					
 | 
				
			||||||
To handle requests, like ["goto definition"], we create an instance of the
 | 
					To handle requests, like ["goto definition"], we create an instance of the
 | 
				
			||||||
`Analysis` and [`schedule`] the task (which consumes `Analysis`) on the
 | 
					`Analysis` and [`schedule`] the task (which consumes `Analysis`) on the
 | 
				
			||||||
@ -186,9 +197,9 @@ executing "goto definition" on the threadpool and a new change comes in, the
 | 
				
			|||||||
task will be canceled as soon as the main loop calls `apply_change` on the
 | 
					task will be canceled as soon as the main loop calls `apply_change` on the
 | 
				
			||||||
`AnalysisHost`.
 | 
					`AnalysisHost`.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
["goto definition"]: https://github.com/rust-lang/rust-analyzer/blob/guide-2019-01/crates/ra_lsp_server/src/main_loop.rs#L296
 | 
					["goto definition"]: https://github.com/rust-lang/rust-analyzer/blob/2024-01-01/crates/rust-analyzer/src/main_loop.rs#L767
 | 
				
			||||||
[`schedule`]: https://github.com/rust-lang/rust-analyzer/blob/guide-2019-01/crates/ra_lsp_server/src/main_loop.rs#L426-L455
 | 
					[`schedule`]: https://github.com/rust-lang/rust-analyzer/blob/2024-01-01/crates/rust-analyzer/src/dispatch.rs#L138
 | 
				
			||||||
[The task]: https://github.com/rust-lang/rust-analyzer/blob/guide-2019-01/crates/ra_lsp_server/src/main_loop/handlers.rs#L205-L223
 | 
					[The task]: https://github.com/rust-lang/rust-analyzer/blob/2024-01-01/crates/rust-analyzer/src/handlers/request.rs#L610-L623
 | 
				
			||||||
 | 
					
 | 
				
			||||||
This concludes the overview of the analyzer's programing *interface*. Next, let's
 | 
					This concludes the overview of the analyzer's programing *interface*. Next, let's
 | 
				
			||||||
dig into the implementation!
 | 
					dig into the implementation!
 | 
				
			||||||
@ -247,16 +258,17 @@ confusing. This illustration by @killercup might help:
 | 
				
			|||||||
## Salsa Input Queries
 | 
					## Salsa Input Queries
 | 
				
			||||||
 | 
					
 | 
				
			||||||
All analyzer information is stored in a salsa database. `Analysis` and
 | 
					All analyzer information is stored in a salsa database. `Analysis` and
 | 
				
			||||||
`AnalysisHost` types are newtype wrappers for [`RootDatabase`] -- a salsa
 | 
					`AnalysisHost` types are essentially newtype wrappers for [`RootDatabase`]
 | 
				
			||||||
database.
 | 
					-- a salsa database.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
[`RootDatabase`]: https://github.com/rust-lang/rust-analyzer/blob/guide-2019-01/crates/ra_ide_api/src/db.rs#L88-L134
 | 
					[`RootDatabase`]: https://github.com/rust-lang/rust-analyzer/blob/2024-01-01/crates/ide-db/src/lib.rs#L69-L324
 | 
				
			||||||
 | 
					
 | 
				
			||||||
Salsa input queries are defined in [`FilesDatabase`] (which is a part of
 | 
					Salsa input queries are defined in [`SourceDatabase`] and [`SourceDatabaseExt`]
 | 
				
			||||||
`RootDatabase`). They closely mirror the familiar `AnalysisChange` structure:
 | 
					(which are a part of `RootDatabase`). They closely mirror the familiar `Change`
 | 
				
			||||||
indeed, what `apply_change` does is it sets the values of input queries.
 | 
					structure: indeed, what `apply_change` does is it sets the values of input queries.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
[`FilesDatabase`]: https://github.com/rust-lang/rust-analyzer/blob/guide-2019-01/crates/ra_db/src/input.rs#L150-L174
 | 
					[`SourceDatabase`]: https://github.com/rust-lang/rust-analyzer/blob/2024-01-01/crates/base-db/src/lib.rs#L58-L65
 | 
				
			||||||
 | 
					[`SourceDatabaseExt`]: https://github.com/rust-lang/rust-analyzer/blob/2024-01-01/crates/base-db/src/lib.rs#L76-L88
 | 
				
			||||||
 | 
					
 | 
				
			||||||
## From text to semantic model
 | 
					## From text to semantic model
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -270,18 +282,18 @@ functions: for example, the same source file might get included as a module in
 | 
				
			|||||||
several crates or a single crate might be present in the compilation DAG
 | 
					several crates or a single crate might be present in the compilation DAG
 | 
				
			||||||
several times, with different sets of `cfg`s enabled. The IDE-specific task of
 | 
					several times, with different sets of `cfg`s enabled. The IDE-specific task of
 | 
				
			||||||
mapping source code into a semantic model is inherently imprecise for
 | 
					mapping source code into a semantic model is inherently imprecise for
 | 
				
			||||||
this reason and gets handled by the [`source_binder`].
 | 
					this reason and gets handled by the [`source_analyzer`].
 | 
				
			||||||
 | 
					
 | 
				
			||||||
[`source_binder`]: https://github.com/rust-lang/rust-analyzer/blob/guide-2019-01/crates/ra_hir/src/source_binder.rs
 | 
					[`source_analyzer`]: https://github.com/rust-lang/rust-analyzer/blob/2024-01-01/crates/hir/src/source_analyzer.rs
 | 
				
			||||||
 | 
					
 | 
				
			||||||
The semantic interface is declared in the [`code_model_api`] module. Each entity is
 | 
					The semantic interface is declared in the [`semantics`] module. Each entity is
 | 
				
			||||||
identified by an integer ID and has a bunch of methods which take a salsa database
 | 
					identified by an integer ID and has a bunch of methods which take a salsa database
 | 
				
			||||||
as an argument and returns other entities (which are also IDs). Internally, these
 | 
					as an argument and returns other entities (which are also IDs). Internally, these
 | 
				
			||||||
methods invoke various queries on the database to build the model on demand.
 | 
					methods invoke various queries on the database to build the model on demand.
 | 
				
			||||||
Here's [the list of queries].
 | 
					Here's [the list of queries].
 | 
				
			||||||
 | 
					
 | 
				
			||||||
[`code_model_api`]: https://github.com/rust-lang/rust-analyzer/blob/guide-2019-01/crates/ra_hir/src/code_model_api.rs
 | 
					[`semantics`]: https://github.com/rust-lang/rust-analyzer/blob/2024-01-01/crates/hir/src/semantics.rs
 | 
				
			||||||
[the list of queries]: https://github.com/rust-lang/rust-analyzer/blob/guide-2019-01/crates/ra_hir/src/db.rs#L20-L106
 | 
					[the list of queries]: https://github.com/rust-lang/rust-analyzer/blob/2024-01-01/crates/hir-ty/src/db.rs#L29-L275
 | 
				
			||||||
 | 
					
 | 
				
			||||||
The first step of building the model is parsing the source code.
 | 
					The first step of building the model is parsing the source code.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -327,7 +339,7 @@ The implementation is based on the generic [rowan] crate on top of which a
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
[libsyntax]: https://github.com/apple/swift/tree/5e2c815edfd758f9b1309ce07bfc01c4bc20ec23/lib/Syntax
 | 
					[libsyntax]: https://github.com/apple/swift/tree/5e2c815edfd758f9b1309ce07bfc01c4bc20ec23/lib/Syntax
 | 
				
			||||||
[rowan]: https://github.com/rust-analyzer/rowan/tree/100a36dc820eb393b74abe0d20ddf99077b61f88
 | 
					[rowan]: https://github.com/rust-analyzer/rowan/tree/100a36dc820eb393b74abe0d20ddf99077b61f88
 | 
				
			||||||
[rust-specific]: https://github.com/rust-lang/rust-analyzer/blob/guide-2019-01/crates/ra_syntax/src/ast/generated.rs
 | 
					[rust-specific]: https://github.com/rust-lang/rust-analyzer/blob/2024-01-01/crates/syntax/src/ast/generated.rs
 | 
				
			||||||
 | 
					
 | 
				
			||||||
The next step in constructing the semantic model is ...
 | 
					The next step in constructing the semantic model is ...
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -336,9 +348,9 @@ The next step in constructing the semantic model is ...
 | 
				
			|||||||
The algorithm for building a tree of modules is to start with a crate root
 | 
					The algorithm for building a tree of modules is to start with a crate root
 | 
				
			||||||
(remember, each `Crate` from a `CrateGraph` has a `FileId`), collect all `mod`
 | 
					(remember, each `Crate` from a `CrateGraph` has a `FileId`), collect all `mod`
 | 
				
			||||||
declarations and recursively process child modules. This is handled by the
 | 
					declarations and recursively process child modules. This is handled by the
 | 
				
			||||||
[`module_tree_query`], with two slight variations.
 | 
					[`crate_def_map_query`], with two slight variations.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
[`module_tree_query`]: https://github.com/rust-lang/rust-analyzer/blob/guide-2019-01/crates/ra_hir/src/module_tree.rs#L115-L133
 | 
					[`crate_def_map_query`]: https://github.com/rust-lang/rust-analyzer/blob/2024-01-01/crates/hir-def/src/nameres.rs#L307-L324
 | 
				
			||||||
 | 
					
 | 
				
			||||||
First, rust-analyzer builds a module tree for all crates in a source root
 | 
					First, rust-analyzer builds a module tree for all crates in a source root
 | 
				
			||||||
simultaneously. The main reason for this is historical (`module_tree` predates
 | 
					simultaneously. The main reason for this is historical (`module_tree` predates
 | 
				
			||||||
@ -347,21 +359,21 @@ part of any crate. That is, if you create a file but do not include it as a
 | 
				
			|||||||
submodule anywhere, you still get semantic completion, and you get a warning
 | 
					submodule anywhere, you still get semantic completion, and you get a warning
 | 
				
			||||||
about a free-floating module (the actual warning is not implemented yet).
 | 
					about a free-floating module (the actual warning is not implemented yet).
 | 
				
			||||||
 | 
					
 | 
				
			||||||
The second difference is that `module_tree_query` does not *directly* depend on
 | 
					The second difference is that `crate_def_map_query` does not *directly* depend on
 | 
				
			||||||
the "parse" query (which is confusingly called `source_file`). Why would calling
 | 
					the `SourceDatabase::parse` query. Why would calling the parse directly be bad?
 | 
				
			||||||
the parse directly be bad? Suppose the user changes the file slightly, by adding
 | 
					Suppose the user changes the file slightly, by adding an insignificant whitespace.
 | 
				
			||||||
an insignificant whitespace. Adding whitespace changes the parse tree (because
 | 
					Adding whitespace changes the parse tree (because it includes whitespace),
 | 
				
			||||||
it includes whitespace), and that means recomputing the whole module tree.
 | 
					and that means recomputing the whole module tree.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
We deal with this problem by introducing an intermediate [`submodules_query`].
 | 
					We deal with this problem by introducing an intermediate [`block_def_map_query`].
 | 
				
			||||||
This query processes the syntax tree and extracts a set of declared submodule
 | 
					This query processes the syntax tree and extracts a set of declared submodule
 | 
				
			||||||
names. Now, changing the whitespace results in `submodules_query` being
 | 
					names. Now, changing the whitespace results in `block_def_map_query` being
 | 
				
			||||||
re-executed for a *single* module, but because the result of this query stays
 | 
					re-executed for a *single* module, but because the result of this query stays
 | 
				
			||||||
the same, we don't have to re-execute [`module_tree_query`]. In fact, we only
 | 
					the same, we don't have to re-execute [`crate_def_map_query`]. In fact, we only
 | 
				
			||||||
need to re-execute it when we add/remove new files or when we change mod
 | 
					need to re-execute it when we add/remove new files or when we change mod
 | 
				
			||||||
declarations.
 | 
					declarations.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
[`submodules_query`]: https://github.com/rust-lang/rust-analyzer/blob/guide-2019-01/crates/ra_hir/src/module_tree.rs#L41
 | 
					[`block_def_map_query`]: https://github.com/rust-lang/rust-analyzer/blob/2024-01-01/crates/hir-def/src/nameres.rs#L326-L354
 | 
				
			||||||
 | 
					
 | 
				
			||||||
We store the resulting modules in a `Vec`-based indexed arena. The indices in
 | 
					We store the resulting modules in a `Vec`-based indexed arena. The indices in
 | 
				
			||||||
the arena becomes module IDs. And this brings us to the next topic:
 | 
					the arena becomes module IDs. And this brings us to the next topic:
 | 
				
			||||||
@ -383,20 +395,23 @@ change the location. However, such "ID" types ceases to be a `Copy`able integer
 | 
				
			|||||||
general can become pretty large if we account for nesting (for example: "third parameter of
 | 
					general can become pretty large if we account for nesting (for example: "third parameter of
 | 
				
			||||||
the `foo` function of the `bar` `impl` in the `baz` module").
 | 
					the `foo` function of the `bar` `impl` in the `baz` module").
 | 
				
			||||||
 | 
					
 | 
				
			||||||
[`LocationInterner`] allows us to combine the benefits of positional and numeric
 | 
					[`Intern` and `Lookup`] traits allows us to combine the benefits of positional and numeric
 | 
				
			||||||
IDs. It is a bidirectional append-only map between locations and consecutive
 | 
					IDs. Implementing both traits effectively creates a bidirectional append-only map
 | 
				
			||||||
integers which can "intern" a location and return an integer ID back. The salsa
 | 
					between locations and integer IDs (typically newtype wrappers of [`salsa::InternId`])
 | 
				
			||||||
database we use includes a couple of [interners]. How to "garbage collect"
 | 
					which can "intern" a location and return an integer ID back. The salsa database we use
 | 
				
			||||||
unused locations is an open question.
 | 
					includes a couple of [interners]. How to "garbage collect" unused locations
 | 
				
			||||||
 | 
					is an open question.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
[`LocationInterner`]: https://github.com/rust-lang/rust-analyzer/blob/guide-2019-01/crates/ra_db/src/loc2id.rs#L65-L71
 | 
					[`Intern` and `Lookup`]: https://github.com/rust-lang/rust-analyzer/blob/2024-01-01/crates/hir-expand/src/lib.rs#L96-L106
 | 
				
			||||||
[interners]: https://github.com/rust-lang/rust-analyzer/blob/guide-2019-01/crates/ra_hir/src/db.rs#L22-L23
 | 
					[interners]: https://github.com/rust-lang/rust-analyzer/blob/2024-01-01/crates/hir-expand/src/lib.rs#L108-L122
 | 
				
			||||||
 | 
					[`salsa::InternId`]: https://docs.rs/salsa/0.16.1/salsa/struct.InternId.html
 | 
				
			||||||
 | 
					
 | 
				
			||||||
For example, we use `LocationInterner` to assign IDs to definitions of functions,
 | 
					For example, we use `Intern` and `Lookup` implementations to assign IDs to
 | 
				
			||||||
structs, enums, etc. The location, [`DefLoc`] contains two bits of information:
 | 
					definitions of functions, structs, enums, etc. The location, [`ItemLoc`] contains
 | 
				
			||||||
 | 
					two bits of information:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
* the ID of the module which contains the definition,
 | 
					* the ID of the module which contains the definition,
 | 
				
			||||||
* the ID of the specific item in the modules source code.
 | 
					* the ID of the specific item in the module's source code.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
We "could" use a text offset for the location of a particular item, but that would play
 | 
					We "could" use a text offset for the location of a particular item, but that would play
 | 
				
			||||||
badly with salsa: offsets change after edits. So, as a rule of thumb, we avoid
 | 
					badly with salsa: offsets change after edits. So, as a rule of thumb, we avoid
 | 
				
			||||||
@ -404,7 +419,7 @@ using offsets, text ranges or syntax trees as keys and values for queries. What
 | 
				
			|||||||
we do instead is we store "index" of the item among all of the items of a file
 | 
					we do instead is we store "index" of the item among all of the items of a file
 | 
				
			||||||
(so, a positional based ID, but localized to a single file).
 | 
					(so, a positional based ID, but localized to a single file).
 | 
				
			||||||
 | 
					
 | 
				
			||||||
[`DefLoc`]: https://github.com/rust-lang/rust-analyzer/blob/guide-2019-01/crates/ra_hir/src/ids.rs#L129-L139
 | 
					[`ItemLoc`]: https://github.com/rust-lang/rust-analyzer/blob/2024-01-01/crates/hir-def/src/lib.rs#L209-L212
 | 
				
			||||||
 | 
					
 | 
				
			||||||
One thing we've glossed over for the time being is support for macros. We have
 | 
					One thing we've glossed over for the time being is support for macros. We have
 | 
				
			||||||
only proof of concept handling of macros at the moment, but they are extremely
 | 
					only proof of concept handling of macros at the moment, but they are extremely
 | 
				
			||||||
@ -424,20 +439,21 @@ enum HirFileId {
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
```
 | 
					```
 | 
				
			||||||
 | 
					
 | 
				
			||||||
`MacroCallId` is an interned ID that specifies a particular macro invocation.
 | 
					`MacroCallId` is an interned ID that identifies a particular macro invocation.
 | 
				
			||||||
Its `MacroCallLoc` contains:
 | 
					Simplifying, it's a `HirFileId` of a file containing the call plus the offset
 | 
				
			||||||
 | 
					of the macro call in the file.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
* `ModuleId` of the containing module
 | 
					Note how `HirFileId` is defined in terms of `MacroCallId` which is defined in
 | 
				
			||||||
* `HirFileId` of the containing file or pseudo file
 | 
					 | 
				
			||||||
* an index of this particular macro invocation in this file (positional id
 | 
					 | 
				
			||||||
  again).
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
Note how `HirFileId` is defined in terms of `MacroCallLoc` which is defined in
 | 
					 | 
				
			||||||
terms of `HirFileId`! This does not recur infinitely though: any chain of
 | 
					terms of `HirFileId`! This does not recur infinitely though: any chain of
 | 
				
			||||||
`HirFileId`s bottoms out in `HirFileId::FileId`, that is, some source file
 | 
					`HirFileId`s bottoms out in `HirFileId::FileId`, that is, some source file
 | 
				
			||||||
actually written by the user.
 | 
					actually written by the user.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
[`HirFileId`]: https://github.com/rust-lang/rust-analyzer/blob/guide-2019-01/crates/ra_hir/src/ids.rs#L31-L93
 | 
					Note also that in the actual implementation, the two variants are encoded in
 | 
				
			||||||
 | 
					a single `u32`, which are differentiated by the MSB (most significant bit).
 | 
				
			||||||
 | 
					If the MSB is 0, the value represents a `FileId`, otherwise the remaining
 | 
				
			||||||
 | 
					31 bits represent a `MacroCallId`.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[`HirFileId`]: https://github.com/rust-lang/rust-analyzer/blob/2024-01-01/crates/span/src/lib.rs#L148-L160
 | 
				
			||||||
 | 
					
 | 
				
			||||||
Now that we understand how to identify a definition, in a source or in a
 | 
					Now that we understand how to identify a definition, in a source or in a
 | 
				
			||||||
macro-generated file, we can discuss name resolution a bit.
 | 
					macro-generated file, we can discuss name resolution a bit.
 | 
				
			||||||
@ -451,13 +467,13 @@ each module into a position-independent representation which does not change if
 | 
				
			|||||||
we modify bodies of the items. After that we [loop] resolving all imports until
 | 
					we modify bodies of the items. After that we [loop] resolving all imports until
 | 
				
			||||||
we've reached a fixed point.
 | 
					we've reached a fixed point.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
[lower]: https://github.com/rust-lang/rust-analyzer/blob/guide-2019-01/crates/ra_hir/src/nameres/lower.rs#L113-L147
 | 
					[lower]: https://github.com/rust-lang/rust-analyzer/blob/2024-01-01/crates/hir-def/src/item_tree.rs#L110-L154
 | 
				
			||||||
[loop]: https://github.com/rust-lang/rust-analyzer/blob/guide-2019-01/crates/ra_hir/src/nameres.rs#L186-L196
 | 
					[loop]: https://github.com/rust-lang/rust-analyzer/blob/2024-01-01/crates/hir-def/src/nameres/collector.rs#L404-L437
 | 
				
			||||||
And, given all our preparation with IDs and a position-independent representation,
 | 
					And, given all our preparation with IDs and a position-independent representation,
 | 
				
			||||||
it is satisfying to [test] that typing inside function body does not invalidate
 | 
					it is satisfying to [test] that typing inside function body does not invalidate
 | 
				
			||||||
name resolution results.
 | 
					name resolution results.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
[test]: https://github.com/rust-lang/rust-analyzer/blob/guide-2019-01/crates/ra_hir/src/nameres/tests.rs#L376
 | 
					[test]: https://github.com/rust-lang/rust-analyzer/blob/2024-01-01/crates/hir-def/src/nameres/tests/incremental.rs#L31
 | 
				
			||||||
 | 
					
 | 
				
			||||||
An interesting fact about name resolution is that it "erases" all of the
 | 
					An interesting fact about name resolution is that it "erases" all of the
 | 
				
			||||||
intermediate paths from the imports: in the end, we know which items are defined
 | 
					intermediate paths from the imports: in the end, we know which items are defined
 | 
				
			||||||
@ -481,21 +497,18 @@ store the syntax node as a part of name resolution: this will break
 | 
				
			|||||||
incrementality, due to the fact that syntax changes after every file
 | 
					incrementality, due to the fact that syntax changes after every file
 | 
				
			||||||
modification.
 | 
					modification.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
We solve this problem during the lowering step of name resolution. The lowering
 | 
					We solve this problem during the lowering step of name resolution. Along with
 | 
				
			||||||
query actually produces a *pair* of outputs: `LoweredModule` and [`SourceMap`].
 | 
					the [`ItemTree`] output, the lowering query additionally produces an [`AstIdMap`]
 | 
				
			||||||
The `LoweredModule` module contains [imports], but in a position-independent form.
 | 
					via an [`ast_id_map`] query. The `ItemTree` contains [imports], but in a
 | 
				
			||||||
The `SourceMap` contains a mapping from position-independent imports to
 | 
					position-independent form based on [`AstId`]. The `AstIdMap` contains a mapping
 | 
				
			||||||
(position-dependent) syntax nodes.
 | 
					from position-independent `AstId`s to (position-dependent) syntax nodes.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
The result of this basic lowering query changes after every modification. But
 | 
					[`ItemTree`]: https://github.com/rust-lang/rust-analyzer/blob/2024-01-01/crates/hir-def/src/item_tree.rs
 | 
				
			||||||
there's an intermediate [projection query] which returns only the first
 | 
					[`AstIdMap`]: https://github.com/rust-lang/rust-analyzer/blob/2024-01-01/crates/hir-expand/src/ast_id_map.rs#L136-L142
 | 
				
			||||||
position-independent part of the lowering. The result of this query is stable.
 | 
					[`ast_id_map`]: https://github.com/rust-lang/rust-analyzer/blob/2024-01-01/crates/hir-def/src/item_tree/lower.rs#L32
 | 
				
			||||||
Naturally, name resolution [uses] this stable projection query.
 | 
					[imports]: https://github.com/rust-lang/rust-analyzer/blob/2024-01-01/crates/hir-def/src/item_tree.rs#L559-L563
 | 
				
			||||||
 | 
					[`AstId`]: https://github.com/rust-lang/rust-analyzer/blob/2024-01-01/crates/hir-expand/src/ast_id_map.rs#L29
 | 
				
			||||||
 | 
					
 | 
				
			||||||
[imports]: https://github.com/rust-lang/rust-analyzer/blob/guide-2019-01/crates/ra_hir/src/nameres/lower.rs#L52-L59
 | 
					 | 
				
			||||||
[`SourceMap`]: https://github.com/rust-lang/rust-analyzer/blob/guide-2019-01/crates/ra_hir/src/nameres/lower.rs#L74-L94
 | 
					 | 
				
			||||||
[projection query]: https://github.com/rust-lang/rust-analyzer/blob/guide-2019-01/crates/ra_hir/src/nameres/lower.rs#L97-L103
 | 
					 | 
				
			||||||
[uses]: https://github.com/rust-lang/rust-analyzer/blob/guide-2019-01/crates/ra_hir/src/query_definitions.rs#L49
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
## Type inference
 | 
					## Type inference
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -517,10 +530,10 @@ construct a mapping from `ExprId`s to types.
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
[@flodiebold]: https://github.com/flodiebold
 | 
					[@flodiebold]: https://github.com/flodiebold
 | 
				
			||||||
[#327]: https://github.com/rust-lang/rust-analyzer/pull/327
 | 
					[#327]: https://github.com/rust-lang/rust-analyzer/pull/327
 | 
				
			||||||
[lower the AST]: https://github.com/rust-lang/rust-analyzer/blob/guide-2019-01/crates/ra_hir/src/expr.rs
 | 
					[lower the AST]: https://github.com/rust-lang/rust-analyzer/blob/2024-01-01/crates/hir-def/src/body.rs
 | 
				
			||||||
[positional ID]: https://github.com/rust-lang/rust-analyzer/blob/guide-2019-01/crates/ra_hir/src/expr.rs#L13-L15
 | 
					[positional ID]: https://github.com/rust-lang/rust-analyzer/blob/2024-01-01/crates/hir-def/src/hir.rs#L37
 | 
				
			||||||
[a source map]: https://github.com/rust-lang/rust-analyzer/blob/guide-2019-01/crates/ra_hir/src/expr.rs#L41-L44
 | 
					[a source map]: https://github.com/rust-lang/rust-analyzer/blob/2024-01-01/crates/hir-def/src/body.rs#L84-L88
 | 
				
			||||||
[type inference]: https://github.com/rust-lang/rust-analyzer/blob/guide-2019-01/crates/ra_hir/src/ty.rs#L1208-L1223
 | 
					[type inference]: https://github.com/rust-lang/rust-analyzer/blob/2024-01-01/crates/hir-ty/src/infer.rs#L76-L131
 | 
				
			||||||
 | 
					
 | 
				
			||||||
## Tying it all together: completion
 | 
					## Tying it all together: completion
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -537,7 +550,7 @@ types (by converting a file url into a numeric `FileId`), [ask analysis for
 | 
				
			|||||||
completion] and serialize results into the LSP.
 | 
					completion] and serialize results into the LSP.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
The [completion implementation] is finally the place where we start doing the actual
 | 
					The [completion implementation] is finally the place where we start doing the actual
 | 
				
			||||||
work. The first step is to collect the `CompletionContext` -- a struct which
 | 
					work. The first step is to collect the [`CompletionContext`] -- a struct which
 | 
				
			||||||
describes the cursor position in terms of Rust syntax and semantics. For
 | 
					describes the cursor position in terms of Rust syntax and semantics. For
 | 
				
			||||||
example, `function_syntax: Option<&'a ast::FnDef>` stores a reference to
 | 
					example, `function_syntax: Option<&'a ast::FnDef>` stores a reference to
 | 
				
			||||||
the enclosing function *syntax*, while `function: Option<hir::Function>` is the
 | 
					the enclosing function *syntax*, while `function: Option<hir::Function>` is the
 | 
				
			||||||
@ -546,26 +559,28 @@ the enclosing function *syntax*, while `function: Option<hir::Function>` is the
 | 
				
			|||||||
To construct the context, we first do an ["IntelliJ Trick"]: we insert a dummy
 | 
					To construct the context, we first do an ["IntelliJ Trick"]: we insert a dummy
 | 
				
			||||||
identifier at the cursor's position and parse this modified file, to get a
 | 
					identifier at the cursor's position and parse this modified file, to get a
 | 
				
			||||||
reasonably looking syntax tree. Then we do a bunch of "classification" routines
 | 
					reasonably looking syntax tree. Then we do a bunch of "classification" routines
 | 
				
			||||||
to figure out the context. For example, we [find an ancestor `fn` node] and we get a
 | 
					to figure out the context. For example, we [find an parent `fn` node], get a
 | 
				
			||||||
[semantic model] for it (using the lossy `source_binder` infrastructure).
 | 
					[semantic model] for it (using the lossy `source_analyzer` infrastructure)
 | 
				
			||||||
 | 
					and use it to determine the [expected type at the cursor position].
 | 
				
			||||||
 | 
					
 | 
				
			||||||
The second step is to run a [series of independent completion routines]. Let's
 | 
					The second step is to run a [series of independent completion routines]. Let's
 | 
				
			||||||
take a closer look at [`complete_dot`], which completes fields and methods in
 | 
					take a closer look at [`complete_dot`], which completes fields and methods in
 | 
				
			||||||
`foo.bar|`. First we extract a semantic function and a syntactic receiver
 | 
					`foo.bar|`. First we extract a semantic receiver type out of the `DotAccess`
 | 
				
			||||||
expression out of the `Context`. Then we run type-inference for this single
 | 
					argument. Then, using the semantic model for the type, we determine if the
 | 
				
			||||||
function and map our syntactic expression to `ExprId`. Using the ID, we figure
 | 
					receiver implements the `Future` trait, and add a `.await` completion item in
 | 
				
			||||||
out the type of the receiver expression. Then we add all fields & methods from
 | 
					the affirmative case. Finally, we add all fields & methods from the type to
 | 
				
			||||||
the type to completion.
 | 
					completion.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
[receiving a message]: https://github.com/rust-lang/rust-analyzer/blob/guide-2019-01/crates/ra_lsp_server/src/main_loop.rs#L203
 | 
					[receiving a message]: https://github.com/rust-lang/rust-analyzer/blob/2024-01-01/crates/rust-analyzer/src/main_loop.rs#L213
 | 
				
			||||||
[schedule it on the threadpool]: https://github.com/rust-lang/rust-analyzer/blob/guide-2019-01/crates/ra_lsp_server/src/main_loop.rs#L428
 | 
					[schedule it on the threadpool]: https://github.com/rust-lang/rust-analyzer/blob/2024-01-01/crates/rust-analyzer/src/dispatch.rs#L197-L211
 | 
				
			||||||
[catch]: https://github.com/rust-lang/rust-analyzer/blob/guide-2019-01/crates/ra_lsp_server/src/main_loop.rs#L436-L442
 | 
					[catch]: https://github.com/rust-lang/rust-analyzer/blob/2024-01-01/crates/rust-analyzer/src/dispatch.rs#L292
 | 
				
			||||||
[the handler]: https://github.com/rust-lang/rust-analyzer/blob/guide-2019-01/crates/ra_lsp_server/src/main_loop/handlers.rs#L304-L343
 | 
					[the handler]: https://github.com/rust-lang/rust-analyzer/blob/2024-01-01/crates/rust-analyzer/src/handlers/request.rs#L850-L876
 | 
				
			||||||
[ask analysis for completion]: https://github.com/rust-lang/rust-analyzer/blob/guide-2019-01/crates/ra_ide_api/src/lib.rs#L439-L444
 | 
					[ask analysis for completion]: https://github.com/rust-lang/rust-analyzer/blob/2024-01-01/crates/ide/src/lib.rs#L605-L615
 | 
				
			||||||
[completion implementation]: https://github.com/rust-lang/rust-analyzer/blob/guide-2019-01/crates/ra_ide_api/src/completion.rs#L46-L62
 | 
					[completion implementation]: https://github.com/rust-lang/rust-analyzer/blob/2024-01-01/crates/ide-completion/src/lib.rs#L148-L229
 | 
				
			||||||
[`CompletionContext`]: https://github.com/rust-lang/rust-analyzer/blob/guide-2019-01/crates/ra_ide_api/src/completion/completion_context.rs#L14-L37
 | 
					[`CompletionContext`]: https://github.com/rust-lang/rust-analyzer/blob/2024-01-01/crates/ide-completion/src/context.rs#L407-L441
 | 
				
			||||||
["IntelliJ Trick"]: https://github.com/rust-lang/rust-analyzer/blob/guide-2019-01/crates/ra_ide_api/src/completion/completion_context.rs#L72-L75
 | 
					["IntelliJ Trick"]: https://github.com/rust-lang/rust-analyzer/blob/2024-01-01/crates/ide-completion/src/context.rs#L644-L648
 | 
				
			||||||
[find an ancestor `fn` node]: https://github.com/rust-lang/rust-analyzer/blob/guide-2019-01/crates/ra_ide_api/src/completion/completion_context.rs#L116-L120
 | 
					[find an parent `fn` node]: https://github.com/rust-lang/rust-analyzer/blob/2024-01-01/crates/ide-completion/src/context/analysis.rs#L463
 | 
				
			||||||
[semantic model]: https://github.com/rust-lang/rust-analyzer/blob/guide-2019-01/crates/ra_ide_api/src/completion/completion_context.rs#L123
 | 
					[semantic model]: https://github.com/rust-lang/rust-analyzer/blob/2024-01-01/crates/ide-completion/src/context/analysis.rs#L466
 | 
				
			||||||
[series of independent completion routines]: https://github.com/rust-lang/rust-analyzer/blob/guide-2019-01/crates/ra_ide_api/src/completion.rs#L52-L59
 | 
					[expected type at the cursor position]: https://github.com/rust-lang/rust-analyzer/blob/2024-01-01/crates/ide-completion/src/context/analysis.rs#L467
 | 
				
			||||||
[`complete_dot`]: https://github.com/rust-lang/rust-analyzer/blob/guide-2019-01/crates/ra_ide_api/src/completion/complete_dot.rs#L6-L22
 | 
					[series of independent completion routines]: https://github.com/rust-lang/rust-analyzer/blob/2024-01-01/crates/ide-completion/src/lib.rs#L157-L226
 | 
				
			||||||
 | 
					[`complete_dot`]: https://github.com/rust-lang/rust-analyzer/blob/2024-01-01/crates/ide-completion/src/completions/dot.rs#L11-L41
 | 
				
			||||||
 | 
				
			|||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user