Linking examples within rustdoc

The Idea

JD and I have been working on a rustdoc extension that automatically links methods to method calls in the examples/ directory of a crate. The goal is to make it easier for people reading documentation to find examples of how to use a method.

Here's what that looks like for the warp::Filter::and method.

By default, the UI shows a single example. The user can click "More examples" to see every instance.

The headings, e.g. examples/returning.rs are links to the corresponding Github page for the example.

How it works

Check out the example-analyzer repo and my rust fork for details. In short, we wrote a script that uses the rustc interface to type check every script in the examples/ directory. We dump a JSON file of every function call mapped to (File, Vec<Span>) pairs. Then rustdoc reads the JSON file and outputs these snippets for every method with invocations in examples/.

Desired feedback

We're posting this here because we eventually want to merge this feature into rustdoc. We'd like to get feedback on the UI (better ways to show the examples?) and on the workflow (how to eventually get this integrated with cargo?). Here are a few concrete technical problems we encountered.

  1. To be able to type-check the examples, we needed a rustc invocation that included all necessary flags like extern libs. With the deprecation of cargo build-plan, we have to hackily scrape that info from stderr by running cargo check. What's a better way to do this?
  2. How do we know where to look for examples? Right now we run cargo check --examples to determine which files are examples, is that good enough?
  3. We have to serialize an identifier for methods that can be passed between the analysis and rustdoc phases. Right now we use a particular way of getting a string name of each method, but is there a more robust means of doing this? eg serializing (DefId, CrateNum) pairs or something.

Any other feedback is welcome!

9 Likes

A lot of these answers depend if you want to keep this as an independent tool or not. I've assumed that you want to merge this into rustdoc and aren't interested in keeping the separate tool.

  • To be able to type-check the examples, we needed a rustc invocation that included all necessary flags like extern libs. With the deprecation of cargo build-plan , we have to hackily scrape that info from stderr by running cargo check . What's a better way to do this?

You should get this 'for free' by running under cargo. You can test today with RUSTC=example-analyzer cargo build --examples.

The hard thing is actually not getting the extern flags, it's convincing cargo that the examples need to be built before documenting the crate and that building an example has outputs that need to be passed as inputs to cargo doc.

  • How do we know where to look for examples? Right now we run cargo check --examples to determine which files are examples, is that good enough?

I would expect Cargo itself to pass this information with --examples=/path/to/example - I don't think it makes sense for cargo to call rustdoc and then rustdoc to again call cargo.

  • We have to serialize an identifier for methods that can be passed between the analysis and rustdoc phases. Right now we use a particular way of getting a string name of each method, but is there a more robust means of doing this? eg serializing (DefId, CrateNum) pairs or something.

I think serializing a DefId would be more robust, but IIRC we tried this last week and it didn't work. I would either ask on Zulip the recommended way to do this or we might even need to ask for a way to serialize custom metadata depending on the command :confused:

Can you serialize tcx.crate_hash(def_id.krate) combined with the tcx.def_path(*def_id).to_string_no_crate_verbose() you are already serializing? CrateNum is a number referencing a crate with a specific SVH (tcx.crate_hash). This mapping is different for each compilation session.

1 Like