Skip to content

Instantly share code, notes, and snippets.

@GuillaumeGomez
Last active June 25, 2021 09:23
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save GuillaumeGomez/66bc7c6424dfe8ffe70739d5634e7f97 to your computer and use it in GitHub Desktop.
Save GuillaumeGomez/66bc7c6424dfe8ffe70739d5634e7f97 to your computer and use it in GitHub Desktop.
Proposal for "go to definition" rustdoc feature

What is the "go to definition" feature?

This feature is about adding links on idents in the source code pages in rustdoc. There have been a lot of concerns about this feature, the point here is to go through all of it, and plan exactly what we want and what we don't want.

I insist on this: I don't want this feature to do anything else than creating links at "compile time". Anything more would very likely be out-of-scope.

Why would we want this feature?

We take as given that "source view" is a valuable part of rustdoc. Sometimes, it's necessary to go beyond the documentation and look at the implementation. That's why source view is a common feature in documentation tooling for a variety of languages. And some of them also provide this feature (like Go for example, you can access this source code from here, or python docs or hexdocs.pm for Elixir and Erlang languages). This is not a rare feature.

Rustdoc's source view often runs into a particular problem: to properly understand the implementation, you need to make reference to identifiers defined elsewhere. For instance, consider the common newtype pattern:

pub MyStruct(OtherStruct)

If you clicked through on MyStruct's src link, expecting to see the private implementation details, you'd be disappointed: you actually need to see the contents of OtherStruct. However, OtherStruct might be in another file entirely. Tracking it down can be a tedious process:

  • Use Ctrl-F to search the page for either a definition of OtherStruct or a use statement that imports it.
  • If you found a use statement, figure out which file it maps to.
  • Open the sidebar and navigate to that file.
  • Ctrl-F again to find the actual definition of OtherStruct.

If OtherStruct itself is defined in terms of additional structs, you may need to repeat this process many times to get even a cursory understanding of the implementation of MyStruct. It can also be error prone. If we implement "go to definition", we can turn this whole process into a single click, and make the result reliable.

The same applies when reading a source code when type annotations are not available:

let var = some_other_var.do_something();
let var2 = a_function();

It'd require to look for each method in the various files, etc.

So overall, this would greatly improve the source code browsing experince.

An example of an extension of this feature would be to have a "go to definition" when clicking on variable names (like var or some_other_var). This sounds like a natural and somewhat "in scope" extension. Or we could go even further and add a "go to type definition". I personally think it's a bit "too much" but not sure what others might think... For now, none of them are handled and I don't plan to add them if this feature is merged.

Similarly, we could also add something to tell us what the type of the variable is. But same answer as previously.

Now let's start with how it works:

Technical details

It uses a HIR visitor going through all files to collect spans where the ident appear as key and put its definition span as definition. The goal being to then use this "span map" to generate links when we generate the source code pages.

As easy as it may seem, it actually has one issue: on methods' call, we need to call typeck_body which generates error messages (even though it doesn't prevent rustdoc to run perfectly normally). It happens when it's being used on blocks which are normally opted-out from the compilation because of a cfg. So sometimes, we ask information about a method which simply doesn't exist in the current cfg. In case it cannot find one, we simply don't generate the span, but the error messages remain. The solution to this problem would be to simply disable error output in this given context.

It doesn't require JS since it's only generating links.

The code for the feature is already well documented.

So on the technical side, there isn't big issues. There is an implementation of this proposal available there: rust-lang/rust#84176

So if not technical, what are the issues?

The biggest concern is actually about "scope expansion". If we add this feature, why not adding others like a "commit source code viewer", or a "git blame source code viewer", or "go to this line in github/gitlab"?

Unfortunately, on this part, the best answer I can provide is that we will need to put clear limits to what we don't want. I think we need to keep rustdoc focused on its target: documentation. We provide source code pages, so this feature allows to browse it more efficiently by using information we already have. Anything more advanced would be very likely out-of-scope.

The big problem being to be able to determine more precisely where is the "conceptual limit": if a contributor in the future comes to you and says "I really want additional source browsing feature X," what is the basis on which you would tell them yes or no?

Another concern is: "why should we implement this in rustdoc? Aren't there already existing tools doing it?".

Github doesn't provide this feature for Rust and cargo-src is old and unmainted and has been for years now. So there aren't many alternatives there unfortunately.

Last concern was about the maintenance.

Any feature has a maintenance cost, this one included. It was suggested to put this feature in its own crate. The only part that can be extracted is the span map builder. It's also very likely that it's the only part which will need "evolutions" because it's using rustc's API. Not sure if it'll make anything simpler or not, to be debated I guess?

@jsha
Copy link

jsha commented Jun 23, 2021

I would start by making the positive case for (a) why this feature is important and (b) why it belongs in rustdoc. For instance it might go something like this:


We take as given that "source view" is a valuable part of rustdoc. Sometimes it's necessary to go beyond the documentation and look at the implementation. That's why source view is a common feature in documentation tooling for a variety of languages.

Rustdoc's source view often runs into a particular problem: to properly understand the implementation, you need to make reference to identifiers defined elsewhere. For instance, consider the common newtype pattern:

pub MyStruct(OtherStruct)

If you clicked through on MyStruct's src link, expecting to see the private implementation details, you'd be disappointed: you actually need to see the contents of OtherStruct. However, OtherStruct might be in another file entirely. Tracking it down can be a tedious process:

  • Use Ctrl-F to search the page for either a definition of OtherStruct or a use statement that imports it.
  • If you found a use statement, figure out which file it maps to.
  • Open the sidebar and navigate to that file.
  • Ctrl-F again to find the actual definition of OtherStruct.

If OtherStruct itself is defined in terms of additional structs, you may need to repeat this process many times to get even a cursory understanding of the implementation of MyStruct. It can also be error prone. If we implement "go to definition," we can turn this whole process into a single click, and make the result reliable.


Also in terms of addressing the concerns about scope creep: I think what we need is a description of the conceptual line between this feature and other, related features. Think of it this way: If a contributor in the future comes to you and says "I really want additional source browsing feature X," what is the basis on which you would tell them yes or no?

@GuillaumeGomez
Copy link
Author

GuillaumeGomez commented Jun 23, 2021

Wow, that's much better indeed, thanks for that!

Also in terms of addressing the concerns about scope creep: I think what we need is a description of the conceptual line between this feature and other, related features. Think of it this way: If a contributor in the future comes to you and says "I really want additional source browsing feature X," what is the basis on which you would tell them yes or no?

I think it's the whole question that we need to answer.

@GuillaumeGomez
Copy link
Author

We mentionned some "source code viewer tools" for other languages, but do you have something more specific in mind by any chance?

@jsha
Copy link

jsha commented Jun 23, 2021

I'm thinking mainly of GitHub and any IDE. Or are you talking about built-in source code view in other langs' docs? Some off the cuff examples are Go: https://pkg.go.dev/database/sql#Register links to https://cs.opensource.google/go/go/+/go1.16.5:src/database/sql/sql.go;l=44 for instance, python docs, and hexdocs.pm. I think it's quite prevalent in languages that have a doc generator!

@GuillaumeGomez
Copy link
Author

I was thinking about other languages' doc generators indeed. Thanks for the links!

@jsha
Copy link

jsha commented Jun 25, 2021

The same applies when reading a source code when type annotations are not available:

let var = some_other_var.do_something();

This makes me wonder: There's a feature in VSCode call "go to type definition", where you can click on a variable name (like var, or some_other_var), and be taken to the definition of that variable's type. That seems like a natural and very useful extension of the "go to definition" feature. In scope or out of scope? :-D Similarly, what about a feature to find out what var's type is?

Overall, doc is looking good; no other feedback.

@GuillaumeGomez
Copy link
Author

I'd say it's out of context, I want to focus on items, not on variables. But up to debate I guess? I'll add a note about that, thanks!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment