rust-lang / rust-lang/rust-analyzer

Monomorphised go to definition

Open
#8,373 1 comment 4 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

A-ide C-feature E-hard fun S-actionable
Dominant language
Rust
Stars
16.9k
Forks
2.2k
Avg merge
1d 12h
Merged PRs (30d)
72

Description

Ideally, we should be able to follow a chain of go-to definitions to drill down to the specific implementations relied upon.

This would happen after https://github.com/rust-analyzer/rust-analyzer/issues/4558 is resolved.

One example is give in https://github.com/rust-analyzer/rust-analyzer/issues/2541#issuecomment-565199436

Larger example I often find myself trying to work out what code gets called in a specific instantiation of a generic function, e.g. in
trait Flob {
    fn flobulate(self); //<-E
}
impl Flob for u32 {
    fn flobulate(self) { //<- D
        some::cool::implementation()
    }
}
fn exec<T: Flob>(val: T) { // <-B
    val.flobulate(); // <-C
}
fn main() {
    exec(10u32); // <-A
}

I would click go to definition on exec at A, which would take me to B. I'd then click go to definition of flobulate at C, which I would want to take me to D, but instead takes me to E. If I want to find out what the specific implementation of flobulate actually does, I have to drop what I'm doing to track this manually. This means tracking which types are used in the instantiation I am interested in, trace them though (following associated types manually) and then track down the specific trait implementation which is actually used.
This is not fun, and feels like something rust-analyzer could help with.

My proposed user experience would be that the generics would be tracked, in a sense monomorphising the generic types upon go to definition.

My super high level implementation suggestion would be:
When you follow a go to definition on a generic function, the generics the function is called with are pushed onto a stack somewhere. Ideally, going back using mouse 4 or alt-left would pop this stack. These generics would then be used to get the specific type information for the first step as far as possible. These types could also be displayed as inlay hints next to the generic declaraction, e.g.

fn exec<T: Flob{inlay hint: =u32}>(val: T) { // <-B
    val.flobulate(); // <-C
}

in my example and

impl<T: fmt::Display + ?Sized{inlay hint: = i32}> ToString for T {
    fn to_string(&self) -> String {
       /* ... */
       buf.write_fmt(format_args!("{}", self))
       /* ... */
    }
}

in the example from https://github.com/rust-analyzer/rust-analyzer/issues/2541#issuecomment-565199436

Most of this is copied from my comment on zulip, where further related discussion might occur.

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start by reading the prerequisite issue #4558, then review the linked examples in issue #2541 and the related Zulip discussion. The work is complete when go-to-definition can carry generic instantiation information through a chain and resolve a trait method to the specific implementation used, with optional generic inlay hints as described.

Written by the indexing model from the issue text.

Assessment

Tech stack
rust
Domain
devtools
Issue type
Feature
Difficulty
5/5
Estimated time
Over a week
Activity status
Stale
Clarity
Mostly clear
Newbie friendliness
25/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.