Here you are 
Question from an IDE to the compiler.
Iâll be using the term âIDEâ for a thing which can semantically analyze source
code and use the results of the analysis to help the user to write code in the
editor. In practical terms, âIDEâ is the backend of RLS (rls-analysis) and
IntelliJ Rust.
IDE maintains complete and precise parse tree, so questions can be formulated in
terms of an AST (but itâs convenient to use spans/offsets to refer to AST
nodes). That is, IDE will ask âwhat declaration does this name refer toâ, but it
wonât ask âwhere are the references in this fileâ (it already knows) or âwhat
attributes are available on this functionâ (itâs easy to calculate
syntactically).
Collect Errors
What are the compiler errors in file âfoo/bar/baz.rsâ?
This query is needed to show errors as the user types.
I think itâs important to restrict the scope of the query to a single file
(errors on demand), to avoid checking the majority of the project, which can be
costly (O(size of the project)) even with fully incremental compilation.
This query can be fully async and not very fast, as it does not block any user
interaction.
From the userâs POV, every error should be accompanied with a quick fix: an
action to automatically fix errors, perhaps with some additional input from the
user. In the ideal world, we would like to keep errors in the compiler and quick
fixes in the IDE (that is, we want to free the compiler from the knowledge of
how to edit and refactor code). Itâs not obvious how to achieve this separation
though: some information which is needed to propose a fix is available just when
compiler decides that thereâs an error.
Resolve Reference
What definition does this reference points to?
I expect this to be the most popular query. IDE will usually ask about all the
references in a single file (to do highlighting) and will often ask about single
references here and there in different files all over the project. This query
must be as fast as possible. Ideally, on the IDE side we would love to see a
synchronous api like reference.resolve() -> Option<Declaration>.
This query should also handle ambiguity. If the name is ambiguous (the name can
mean two different method from different traits, and UFCS is needed), it should
be possible to get all possible targets: reference.multi_resolve() -> Vec<Declaration>. This should also handle use declarations, which can imoprt a
name from different namespaces.
Note that the compiler does not need to handle the reverse query. That is, âfind
usagesâ can (and probably should) be handled by the IDE. The IDE maintains a
text index of all references in project (cheap to maintain, because it depends
only on the syntactic contents of a single file). For find usages, IDE finds the
list of candidates using this text index, and then filters the candidates
invoking compilerâs âResolve Referenceâ query (it should be fast, and again even
for ârandom accessâ it should be possible to avoid analyzing the majority of the
project).
Collect Visible Names
What can be the name of this reference such that it resolves to anything?
That is, for the code
struct S;
impl S {
fn foo(&self) {}
fn bar(&self) {}
}
fn f(s: S) {
s.foo // <- `.foo` is the reference we will be asking about
}
the answer should be [foo, bar]. Basically, this question gives completion
variants without filtering by actual reference name. It can be seen as a
more general version of the âResolve Referenceâ query. In fact, in IntelliJ
resolve reference is usually implemented as âCollect Visible Namesâ followed by
filtering by the actual method name.
This question will be asked about at most one identifier in the file, it should
be reasonably fast, but can be async (ideally, some names are available
immediately, and the others are supplied in the async fashion).
What If Query
The queries should be available in the âwhat ifâ mode. That is, IDE should be
able to inject some Rust code fragment anywhere into the existing project, and
ask questions about it. It can be trivially implemented as âmodify file, ask
question, rollback modificationâ, but it makes sense to build in some support
for this, because you can avoid a lot of invalidation work (and "what if"
queries may be asked in the context that blocks userâs actions, so they need to
be as fast as possible).
An example of what if query would be a completion request for the following
code, with the caret just after the dot.
fn f(s: S) {
s.
}
Here, the IDE canât ask âCollect Visible Namesâ query, because thereâs no
reference yet. So, IDE inserts a dummy identifier
fn f(s: S) {
s.rust_rulezz
}
and asks âCollect Visible Namesâ for it.
Type related queries
What is the type of this expression?
Does this type implements this trait?
These queries will be a little awkward because the answer is not just some node
in the existing AST, but some representation of the thing that exists only in
compilerâs memory.
I am not sure about What traits are implemented by this type query. This is
a bad type of âsearchâ query, which requires looking at the whole project, but
looks like it must be implemented in the compiler anyway to do âCollect visible
namesâ (last time I looked, the compiler filtered the candidate traits by the
name of the method resolved, but this wonât work for completion).