Overview

For non-Cargo projects, rust-analyzer depends on either a rust-project.json file at the root of the project that describes its structure or on build system specific project auto-discovery. The rust_analyzer rules facilitate both approaches.

rust-project.json approach

Setup

First, ensure rules_rust is setup in your workspace. By default, rust_register_toolchains will ensure a rust_analyzer_toolchain is registered within the WORKSPACE.

Next, load the dependencies for the rust-project.json generator tool:

load("@rules_rust//tools/rust_analyzer:deps.bzl", "rust_analyzer_dependencies")

rust_analyzer_dependencies()

Finally, run bazel run @rules_rust//tools/rust_analyzer:gen_rust_project whenever dependencies change to regenerate the rust-project.json file. It should be added to .gitignore because it is effectively a build artifact. Once the rust-project.json has been generated in the project root, rust-analyzer can pick it up upon restart.

For users who do not use rust_register_toolchains to register toolchains, the following can be added to their WORKSPACE to register a rust_analyzer_toolchain. Please make sure the Rust version used in this toolchain matches the version used by the currently registered toolchain or the sources/documentation will not match what's being compiled with and can lead to confusing results.

load("@rules_rust//rust:repositories.bzl", "rust_analyzer_toolchain_repository")

register_toolchains(rust_analyzer_toolchain_repository(
    name = "rust_analyzer_toolchain",
    # This should match the currently registered toolchain.
    version = "1.63.0",
))

VSCode

To set this up using VSCode, users should first install the rust_analyzer plugin. With that in place, the following task can be added to the .vscode/tasks.json file of the workspace to ensure a rust-project.json file is created and up to date when the editor is opened.

{
    "version": "2.0.0",
    "tasks": [
        {
            "label": "Generate rust-project.json",
            "command": "bazel",
            "args": [
                "run",
                "@rules_rust//tools/rust_analyzer:gen_rust_project"
            ],
            "options": {
                "cwd": "${workspaceFolder}"
            },
            "group": "build",
            "problemMatcher": [],
            "presentation": {
                "reveal": "never",
                "panel": "dedicated"
            },
            "runOptions": {
                "runOn": "folderOpen"
            }
        },
    ]
}

Alternative vscode option (prototype)

Add the following to your bazelrc:

build --@rules_rust//rust/settings:rustc_output_diagnostics=true --output_groups=+rust_lib_rustc_output,+rust_metadata_rustc_output

Then you can use a prototype rust-analyzer plugin that automatically collects the outputs whenever you recompile.

Project auto-discovery

Setup

Auto-discovery makes rust-analyzer behave in a Bazel project in a similar fashion to how it does in a Cargo project. This is achieved by generating a structure similar to what rust-project.json contains but, instead of writing that to a file, the data gets piped to rust-analyzer directly through stdout. To use auto-discovery the rust-analyzer IDE settings must be configured similar to:

"rust-analyzer": {
    "workspace": {
        "discoverConfig": {
            "command": ["discover_bazel_rust_project.sh"],
            "progressLabel": "rust_analyzer",
            "filesToWatch": ["BUILD", "BUILD.bazel", "MODULE.bazel"]
        }
    }
}

The shell script passed to discoverConfig.command is typically meant to wrap the bazel rule invocation, primarily for muting stderr (because rust-analyzer will consider that an error has occurred if anything is passed through stderr) and, additionally, for specifying rule arguments. E.g:

#!/usr/bin/bash

bazel \
    run \
    @rules_rust//tools/rust_analyzer:discover_bazel_rust_project -- \
    --bazel_startup_option=--output_base=~/ide_bazel \
    --bazel_arg=--watchfs \
    ${1:+"$1"} 2>/dev/null

The script above also handles an optional CLI argument which gets passed when workspace splitting is enabled. The script path should be either absolute or relative to the project root.

Workspace splitting

The above configuration treats the entire project as a single workspace. However, large codebases might be too much to handle for rust-analyzer all at once. This can be addressed by splitting the codebase in multiple workspaces by extending the discoverConfig.command setting:

"rust-analyzer": {
    "workspace": {
        "discoverConfig": {
            "command": ["discover_bazel_rust_project.sh", "{arg}"],
            "progressLabel": "rust_analyzer",
            "filesToWatch": ["BUILD", "BUILD.bazel", "MODULE.bazel"]
        }
    }
}

{arg} acts as a placeholder that rust-analyzer replaces with the path of the source / build file that gets opened.

The root of the workspace will, in this configuration, be the package the crate currently being worked on belongs to. This means that only that package and its dependencies get built and indexed by rust-analyzer, thus allowing for a smaller footprint.

rust-analyzer will switch workspaces whenever an out-of-tree file gets opened, essentially indexing that crate and its dependencies separately. A caveat of this is that dependents of the crate currently being worked on are not indexed and won't be tracked by rust-analyzer.

Aspects

rust_analyzer_aspect

load("@rules_rust//rust:defs.bzl", "rust_analyzer_aspect")

rust_analyzer_aspect()

Annotates rust rules with RustAnalyzerInfo later used to build a rust-project.json

ASPECT ATTRIBUTES

NameType
depsString
proc_macro_depsString
crateString
actualString
protoString

ATTRIBUTES