2019 Strategy for Rustc and the RLS

I don’t see this as a problem. Effectively (and this will require some work in Cargo) we will ask Cargo how to build a project and then have a very quick way to query "this file has changed what should be rebuilt) and then we do it. Other build systems will use Cargo as an abstraction layer. You never have to do a full build unless you want to (or say the Cargo.toml is changed).

This is the principal issue we disagree on. I'll address only it, but thoroughly.

Let's start with definitions, to agree precisely on what we disagree about.

Build Process is a sequence of tools invocations which produce project artifacts. Each sufficiently large project has it's own slightly unique build process.

Project Model is a logical model of dependencies between various source files. Unlike a per-project Build Process, Project Model is fixed for a language. For Java, the model is classpath, for Rust, the model is a DAG of anonymous crates with attributed edges.

Now we can formulate the two approaches whose merits we are debating.

Approach 1: Code Analyzer should instrument the Build Process, intercept compiler calls and work from that.

Approach 2: Code Analyzer should be build-system and Build Process agnostic and instead work with Project Model.

RLS current architecture and current roadmap focuses on the first approach. I am proponent of the second one.

Now, with definitions sorted out, let me argue for the second approach.

The main argument for approach 1 is correctness: there's an assumption that, to get analysis 100% correct, Code Analyzer should exactly repeat what compiler does.

There are two problems with this argument.

First, as eloquently expressed by @petrochenkov in a sibling thread, building large projects is hard, which results in Code Analyzer working great for small stuff, but being completely helpless for entreprisy use-cases.

Second, the assumption "you need to mimic compiler exactly to be precise" does not hold in practice. Quoting myself from that other thread,

As another point for “you can have a horrendous build system and precise Code Analyzer”, consider IntelliJ itself. It is a huge project, and I think it is build with every tool you can use to build Java. I definitely saw Ant, Maven, Gant, Gradle and JPS in various parts of it. However, IntelliJ is developed in IntelliJ and, naturally, all code insight features just work: IDEA knows little about it’s build system, it only has a project model which is a set of XML files describing classpaths of various modules. Now, syncing those XML files and build-system is a pain. However in Rust a similar task would be much simpler: it has a stronger project model and it has a mostly one build system (Cargo).

As for positive arguments for the approach 2, there are three of them.

First and foremost, it's more reliable: getting a Project Model is much simpler than intercepting Build Process, so Code Analyzer works at least partially with any code-base.

It's also more performance. It's true that you can achieve good incrementallity using approach 1. However, the main requirement for Code Analyzer is not incrementality, but on-demandness, and that I believe is incompatible with approach 1.

Finally, nobody does approach 1 :slight_smile:

For Java, there are a ton of different build systems but IntelliJ is not married to any particular one, and happily works with various mixtures.

Kotlin also is build system agnostic. I think currently Kotlin itself is build with Gradle, but up until recently its build system was a zoo of nightmares as well (like, you had to have three different JDKs installed), which haven't preventing the IDE from doing completions, type inference, and all other IDE stuff.

As linked above, Dart Analyzer also does not care much about Build Process.

The only project that I know of that does Build Process instrumentation is Kythe. And that makes perfect sense for Kythe, which is geared towards offline code indexing, DXR style, and not towards working with red-hot freshly typed code.

10 Likes