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).