rust-lang/rust

Tracking issue for RFC 1946 - intra-rustdoc links

nrc opened this issue · 161 comments

nrc commented

RFC: https://github.com/rust-lang/rfcs/blob/master/text/1946-intra-rustdoc-links.md
RFC PR: rust-lang/rfcs#1946

Todo:

  • Land #47046
  • Make it work with cross-crate imports (#65983, #47046 (comment))
  • Make it work for proc macros #73173
  • Make it warn more on resolution errors (#49542)
  • Make warnings/errors use better spans than the first line of docs
    • first step: attribute (#51111)
    • point to actual link in doc comment (#51391)
    • Add lint for link resolution failure (#51382)
  • Implied Shortcut Reference Links (#48335)
  • Enum variants and UFCS/associated methods (#47701)
  • Primitives (#49459)
  • Use the right resolution scopes for inner doc comments on things that aren't modules (#55364)
  • Handle fields and variants (#49512)
    • Handle fields inside struct variants (#66704)
  • Support Self (#49583)
  • Write documentation (#66000)
  • Stabilization (#74430 - current impl is gated to only run on nightly, but does not require a feature gate)
  • Handle methods from traits implementations
  • Support anchors (#66675)
  • Support missing primitive types (such as slices) (#63351)
  • Allow referring to Self::item (for cmp on Ord)
  • Fix current crate linking when no module is provided in the path (for example, Foo won't link to the crate's Foo type for some reason)
  • doc(hidden) reexports can break intra-doc links (#73363)
  • Support associated items in contexts other than documenting a trait method (#74489)

Other related issues:

  • Suppress privacy errors with document-private-types (#74134, fix at #74147)
  • provide option to turn intra-link resolution errors into proper errors (#50082)

Preliminary implementation notes:

Getting just the links from the markdown is relatively straightforward. You'll need to do this twice, to capture the behavior in both the Hoedown and Pulldown markdown renderers. Thankfully, each has a way of capturing links as they are parsed from the document. (While capturing the link you can also look for the "struct/enum/mod/macro/static/etc" marker to aid in resolution down the line.)

In Hoedown, the render function in src/librustdoc/html/markdown.rs captures all the events as they are captured. Hoedown uses a system of registering callbacks on the renderer configuration to capture each event separately. The link event and its related linkfn function pointer signature are already present in the file, so you would need to create and use that callback to capture and modify links as they appear.

For Pulldown, the parser is surfaced as an Iterator of events. There are already a handful of iterator wrappers present in html/markdown.rs, so adding another one (and using it in the impl fmt::Display for Markdown farther down) shouldn't be a big deal. I'm... honestly not that familiar with how Pulldown represents link definitions in its Event/Tag enums, so more research would be needed to effectively get link text for that.

Now, with the links in hand, you need to do two things, neither of which i know offhand since they involve interfacing with the compiler:

  • Attempt to parse the link as a path (if it's not a valid Rust path, just emit the URL as-is and skip the rest of this)
  • Resolve the path in the "current" module (emit a warning if resolution fails)

Presumably there's a way to make the resolution come up with a DefId? Even after cleaning the AST, rustdoc still deals in those, so if that's the case there are ways to map that to the proper relative link.

I've got a WIP going on in https://github.com/manishearth/rust/tree/correct-resolver-fail , which works off of Misdreavus' WIP (they did most of the work).

Note that that branch is now live as #47046, which i'm polishing and finishing up.

Note for people watching this thread but not #47046: We're trying to wind that PR down to get a minimal version landed, so an initial implementation is fairly close.

sgrif commented

One thing that appears to not work in #47046 (and I don't think is ever explicitly addressed in the RFC) is how to link to primitives like u32 or bool.

Yeah I have to investigate that

@killercup @QuietMisdreavus should we turn the error into a warning ? I don't like the idea of crate docs breaking because someone introduced a name into a glob import

@Manishearth does rustdoc have some idea of "lint levels"? What are the current deprecation warnings? Just println!s?

We can just struct_span_warn instead. rustc can emit warnings not tied to actual warning names. There are no lint levels.

Shortcut links being implemented at #48335

If [std::convert::Into] will work as part of this RFC, will [std::convert::Into#generic-implementations] also work for linking to specific sections of that documentation?

ehuss commented

I can't seem to get links to struct fields to work (either [MyStruct.field] or [MyStruct::field]). Am I missing something, or is it not implemented? If not, is it planned to be supported?

MyStruct::field should work

Oh, wait, no, struct fields don't work yet. We can add those later.

Was their any discussion about linking to dev-dependencies? For example in a facade heavy crate like futures it may be that some sub-crates want to link back to the prelude at ::futures::prelude directly. One way this could be done is simply including dev-dependencies in the doc build and relying on the implicit use of crates in Edition 2018.

Edge-case error I found: apparently, if a crate is marked as no_std, then this also applies to autolinks; I can't link f64::powi in a no_std crate even if the crate itself doesn't need it. This seems like a relatively easy bug to fix imho.

Based on a conversation just now: It would be cool if we could add in a section to pages to collect "back-links", or a listing of pages that contain doc-links to a given page.

One thing I would really love was if [method] in the documentation for some type T linked to [T::method] automatically. It makes it a little easier to write documentation text that flows nicely and doesn't seem awkward (I know you're talking about a method on T, that's the text I'm reading!)

@jonhoo

That would be ambiguous though, if a top level item existed with the same name.

I would personally be happy if [Self::method] worked.

Maybe [method][T::method] works today? (As in, [text shown][link-or-reference-identifier].)

We could establish a priority to resolve ambiguities.

@crumblingstatue Self::method is nice, though I would want it to be rendered as just method. This shows up a lot in documentation of the form:

/// To make a new `Foo`, you likely want to use [`from_bar`] to wrap an existing [`Bar`].
/// You can also use [`mock`] to get a fake `Foo` for use in tests.
struct Foo;

If these cases, Foo::from_bar and Foo::mock would seem a bit redundant, and make the sentence harder to follow.

@jonhoo I think it should be fine if it was written as [Self::method], as it is unambiguous, but rustdoc would recognize that it's a method, and rendered it as just method. If the user really wanted to keep the Self:: part in the rendered version for some reason, they could just write [Self::method](Self::method).

EDIT: Jeezus, I was on phone, and couldn't find the backtick on the virtual keyboard.

In the futures crate we get a lot of intra doc resolution warnings. The curious thing is that these warnings are always for another crate than the crate for which the docs are currently being built.

E.g. futures-util-preview on its own builds with no warnings. However, futures-executor-preview, which depends on futures-util-preview, builds with warnings from futures-util-preview.

https://travis-ci.org/rust-lang-nursery/futures-rs/jobs/412557090

Should "point to actual link in doc comment" be checked off since #51391 is merged?

Also "Add lint for link resolution failure" (#51382)

And looks like "Support Self" was added in #52368.

Checked the 3 of them.

I'm finding a resolution failure that is puzzling me:

pub struct Foo {}

impl Foo {
    /// [`baz`]
    pub fn bar() {}

    /// [`bar`](Self::bar)
    pub fn baz() {}
}

Neither bar nor baz resolve.

It makes sense to me that [`baz`] wouldn't resolve - the name in-scope is Foo::baz (since it hasn't been pulled in by a use statement) so that seems fine to me.

That Self::bar doesn't resolve either seems odd. @GuillaumeGomez Does self resolution work when written on associated methods?

Self doesn't resolve the same way IIRC, so we'd have to explicitly support it

This is only tangentially related, but is there any way at all we could help improve the situation for README's that are generated from rustdoc with this change? I'm thinking specifically of webern/cargo-readme#18, though I'm sure there are other cases where the relative links won't resolve. Could we generate absolute links instead based on the documentation key in Cargo.toml (if present)?

Here's another interesting issue:

If you take the module docs in liballoc/rc.rs and remove the //! [`Rc`]: struct.Rc.html line, you get a bunch of resolution failures. However, I'm having trouble reproducing this in a small example, so I wonder if it's an artifact of how std is organized. Does this have to do with the cross-crate imports bullet point above?

When building docs with deny warnings on nightly, currently you get a bunch of errors related to intra rustdoc links not being resolvable. It looks like the errors are coming from building docs for the standard library itself.

Can we add something to the Rust CI to check for these warnings, or otherwise disable issuing these warnings when building downstream crates?

Example:

➜  futures-rs git:(update) ✗ RUSTDOCFLAGS=-Dwarnings cargo doc --all --exclude futures-preview --exclude futures-executor-preview --exclude futures-test-preview
 Documenting futures-util-preview v0.3.0-alpha.6 (/usr/local/google/home/cramertj/src/futures-rs/futures-util)
error: `[SocketAddrV4]` cannot be resolved, ignoring it...                                                
    |                                                                                                     
    = note: `-D intra-doc-link-resolution-failure` implied by `-D warnings`                               
    = note: the link appears in this line:                                                                
                                                                                                          
             Converts a [`SocketAddrV4`] into a [`SocketAddr::V4`].                                       
                         ^^^^^^^^^^^^^^                                                                   
    = help: to escape `[` and `]` characters, just add '\' before them like `\[` or `\]`                  
                                                                                                          
error: `[SocketAddr::V4]` cannot be resolved, ignoring it...                                              
    |                                                                                                     
    = note: the link appears in this line:                                                                
                                                                                                          
             Converts a [`SocketAddrV4`] into a [`SocketAddr::V4`].                                       
                                                 ^^^^^^^^^^^^^^^^                                         
    = help: to escape `[` and `]` characters, just add '\' before them like `\[` or `\]`                  
                                                                                                          
error: `[Mutex::new]` cannot be resolved, ignoring it...                                                  
    |                                                                                                     
    = note: the link appears in this line:                                                                
                                                                                                          
             This is equivalent to [`Mutex::new`].                                                        
                                    ^^^^^^^^^^^^                                                          
    = help: to escape `[` and `]` characters, just add '\' before them like `\[` or `\]`                  
                                                                                                          
error: `[SocketAddrV6]` cannot be resolved, ignoring it...                                                
    |                                                                                                     
    = note: the link appears in this line:                                                                
                                                                                                          
             Converts a [`SocketAddrV6`] into a [`SocketAddr::V6`].                                       
                         ^^^^^^^^^^^^^^                                                                   
    = help: to escape `[` and `]` characters, just add '\' before them like `\[` or `\]`                  
                                                                                                          
error: `[SocketAddr::V6]` cannot be resolved, ignoring it...                                              
    |                                                                                                     
    = note: the link appears in this line:                                                                
                                                                                                          
             Converts a [`SocketAddrV6`] into a [`SocketAddr::V6`].                                       
                                                 ^^^^^^^^^^^^^^^^                                         
    = help: to escape `[` and `]` characters, just add '\' before them like `\[` or `\]`                  
                                                                                                          
error: `[OsString]` cannot be resolved, ignoring it...                                                    
    |                                                                                                     
    = note: the link appears in this line:                                                                
                                                                                                          
             Converts a [`String`] into a [`OsString`].                                                   
                                           ^^^^^^^^^^                                                     
    = help: to escape `[` and `]` characters, just add '\' before them like `\[` or `\]`                  
                                                                                                          
error: `[IpAddr]` cannot be resolved, ignoring it...                                                      
    |                                                                                                     
    = note: the link appears in this line:                                                                
                                                                                                          
             Converts a tuple struct (Into<[`IpAddr`]>, `u16`) into a [`SocketAddr`].                     
                                            ^^^^^^^^                                                      
    = help: to escape `[` and `]` characters, just add '\' before them like `\[` or `\]`                  
                                                                                                          
error: `[SocketAddr]` cannot be resolved, ignoring it...                                                  
    |                                                                                                     
    = note: the link appears in this line:                                                                
                                                                                                          
             Converts a tuple struct (Into<[`IpAddr`]>, `u16`) into a [`SocketAddr`].                     
                                                                       ^^^^^^^^^^^^                       
    = help: to escape `[` and `]` characters, just add '\' before them like `\[` or `\]`                  
                                                                                                          
error: `[SocketAddr::V4]` cannot be resolved, ignoring it...                                              
    |                                                                                                     
    = note: the link appears in this line:                                                                
                                                                                                          
             This conversion creates a [`SocketAddr::V4`] for a [`IpAddr::V4`]                            
                                        ^^^^^^^^^^^^^^^^                                                  
    = help: to escape `[` and `]` characters, just add '\' before them like `\[` or `\]`                  
                                                                                                          
error: `[IpAddr::V4]` cannot be resolved, ignoring it...                                                  
    |                                                                                                     
    = note: the link appears in this line:                                                                
                                                                                                          
             This conversion creates a [`SocketAddr::V4`] for a [`IpAddr::V4`]                            
                                                                 ^^^^^^^^^^^^                             
    = help: to escape `[` and `]` characters, just add '\' before them like `\[` or `\]`                  
                                                                                                          
error: `[SocketAddr::V6]` cannot be resolved, ignoring it...                                              
    |                                                                                                     
    = note: the link appears in this line:                                                                
                                                                                                          
             and creates a [`SocketAddr::V6`] for a [`IpAddr::V6`].                                       
                            ^^^^^^^^^^^^^^^^                                                              
    = help: to escape `[` and `]` characters, just add '\' before them like `\[` or `\]`                  
                                                                                                          
error: `[IpAddr::V6]` cannot be resolved, ignoring it...                                                  
    |                                                                                                     
    = note: the link appears in this line:                                                                
                                                                                                          
             and creates a [`SocketAddr::V6`] for a [`IpAddr::V6`].                                       
                                                     ^^^^^^^^^^^^                                         
    = help: to escape `[` and `]` characters, just add '\' before them like `\[` or `\]`                  
                                                                                                          
error: `[SocketAddr]` cannot be resolved, ignoring it...                                                  
    |                                                                                                     
    = note: the link appears in this line:                                                                
                                                                                                          
             `u16` is treated as port of the newly created [`SocketAddr`].                                
                                                            ^^^^^^^^^^^^                                  
    = help: to escape `[` and `]` characters, just add '\' before them like `\[` or `\]`                  
                                                                                                          
error: `[RwLock::new]` cannot be resolved, ignoring it...                                                 
    |                                                                                                     
    = note: the link appears in this line:                                                                
                                                                                                          
             This is equivalent to [`RwLock::new`].                                                       
                                    ^^^^^^^^^^^^^                                                         
    = help: to escape `[` and `]` characters, just add '\' before them like `\[` or `\]`                  
                                                                                                          
error: `[DefaultHasher::new]` cannot be resolved, ignoring it...                                          
     |                                                                                                    
     = note: the link appears in this line:                                                               
                                                                                                          
              Creates a new `DefaultHasher` using [`new`][DefaultHasher::new].                            
                                                          ^^^^^^^^^^^^^^^^^^                              
     = help: to escape `[` and `]` characters, just add '\' before them like `\[` or `\]`                 
                                                                                                          
error: `[Weak]` cannot be resolved, ignoring it...                                                        
    |                                                                                                     
    = note: the link appears in this line:                                                                
                                                                                                          
             [`Weak`], so we `drop` the inner value.                                                      
              ^^^^^^                                                                                      
    = help: to escape `[` and `]` characters, just add '\' before them like `\[` or `\]`                  
                                                                                                          
error: `[Weak]` cannot be resolved, ignoring it...                                                        
    |                                                                                                     
    = note: the link appears in this line:                                                                
                                                                                                          
             [`Weak`], so we `drop` the inner value.                                                      
              ^^^^^^                                                                                      
    = help: to escape `[` and `]` characters, just add '\' before them like `\[` or `\]`                  
                                                                                                          
error: `[Weak::upgrade]` cannot be resolved, ignoring it...                                               
     |                                                                                                    
     = note: the link appears in this line:                                                               
                                                                                                          
              Calling [`upgrade`][Weak::upgrade] on the return value always                               
                                  ^^^^^^^^^^^^^                                                           
     = help: to escape `[` and `]` characters, just add '\' before them like `\[` or `\]`                 
                                                                                                          
error: `[Weak::upgrade]` cannot be resolved, ignoring it...                                               
     |                                                                                                    
     = note: the link appears in this line:                                                               
                                                                                                          
              it. Calling [`upgrade`][Weak::upgrade] on the return value always gives [`None`].           
                                      ^^^^^^^^^^^^^                                                       
     = help: to escape `[` and `]` characters, just add '\' before them like `\[` or `\]`                 
                                                                                                          
error: Could not document `futures-util-preview`.                                                         

Caused by:
  process didn't exit successfully: `rustdoc --edition=2018 --crate-name futures_util futures-util/src/lib.rs --color always -o /usr/local/google/home/cramertj/src/futures-rs/target/doc --cfg 'feature="default"' --cfg 'feature="either"' --cfg 'feature="futures-core-preview"' --cfg 'feature="futures-io-preview"' --cfg 'feature="futures-sink-preview"' --cfg 'feature="slab"' --cfg 'feature="std"' -L dependency=/usr/local/google/home/cramertj/src/futures-rs/target/debug/deps --extern either=/usr/local/google/home/cramertj/src/futures-rs/target/debug/deps/libeither-758c45227b7be9c5.rmeta --extern futures_channel=/usr/local/google/home/cramertj/src/futures-rs/target/debug/deps/libfutures_channel-65413d051bb1740e.rmeta --extern futures_core=/usr/local/google/home/cramertj/src/futures-rs/target/debug/deps/libfutures_core-46ea778441081a49.rmeta --extern futures_io=/usr/local/google/home/cramertj/src/futures-rs/target/debug/deps/libfutures_io-bb7a8db4df7a5605.rmeta --extern futures_sink=/usr/local/google/home/cramertj/src/futures-rs/target/debug/deps/libfutures_sink-20c7bbdd565b6353.rmeta --extern pin_utils=/usr/local/google/home/cramertj/src/futures-rs/target/debug/deps/libpin_utils-9603bf9d2932b1e6.rmeta --extern slab=/usr/local/google/home/cramertj/src/futures-rs/target/debug/deps/libslab-6bf6029d2c4c1117.rmeta -Dwarnings` (exit code: 1)

@cramertj I was actually putting together a PR to deny the warnings, but it appears that some of the existing failures are caused by issues in the implementation rather than errors in the documentation. So, like you said, it might be best to silence them when documenting downstream crates for now.

I assume that "Make it work with cross-crate imports" in the OP is referring to things like docs on externally-defined trait implementations failing to resolve and causing warnings to appear, even though the docs are valid in the crate that defined the trait? Is this still blocked on something else (and if so, is there somewhere with more details for tracking it than a comment on the old PR)?

The issue @MajorBreakfast is hitting in #43466 (comment) is still happening and is a serious blocker for me-- even when building with --no-deps, I'm getting warnings and errors from upstream documentation resolution failures.

(am unblocked through the magic of RUSTDOC env var, shell scripts, and the --cap-lints setting, but still this should get fixed XD)

[dependencies]
futures-preview = "0.3.0-alpha.7"
#![deny(warnings)]

extern crate futures;

This is enough to trigger a failure right now. (I'll need to narrow it down, but using a crate with some specific use of intra-links will work.) I thought i'd fixed this kind of erroneous error with #53162 (comment) but it turns out it wasn't good enough. I'm building/testing a fix right now that refuses to run link collection on non-local items. It may be a bit drastic (and breaks docs which actually work in this situation) but it finally silence this kind of issue until we can actually support them.

Narrowed it down:

// inner crate

use std::fmt::Debug;

pub trait SomeTrait {}

/// this is an impl for [SomeTrait]
impl<T: Debug> SomeTrait for T {}

I bet the PR i linked doesn't touch this impl because it's generic, so it could apply to any number of local items.

In Rocket, we have two libraries in a workspace: a core library, rocket, and a contrib library rocket_contrib. rocket_contrib is not a dependency of rocket, but rocket refers to rocket_contrib in its documentation often.

Because rocket_contrib is not a dependency of rocket, cross-crate links are not resolving:

This is [rocket_contrib], a broken link.

Is there any workaround to this? I've tried adding --extern rocket_contrib=path/to.rmeta manually via RUSTDOCFLAGS, as well as using -Z unstable-options --extern-html-root-url rocket_contrib=https://rocket-contrib.docs, but neither work. Even if the latter worked, it isn't a great solution as it'll produce absolute links, partially defeating the purpose.

In general, I think cargo and/or rustdoc should automatically add all crates in the same workspace to the resolution domain for inter-crate links, making something like [rocket_contrb] work out of the box.

@SergioBenitez, have you tried adding a docs feature to the rocket lib that you set when building docs and that enables an optional dependency on rocket_contrib?

(Sorry to respond here without having followed this issue or impl much!)

automatically add all crates in the same workspace to the resolution domain for inter-crate links, making something like [rocket_contrb] work out of the box.

This will cause issues with the docs when built outside the workspace, e.g. when someone runs cargo doc -p rocket --open to get locally built docs in their project. Changing RUSTDOCFLAGS or having an optional feature has similar problems.

Earlier I suggested including dev-dependencies during doc builds to solve the same issue, or maybe there could be an additional doc-dependencies section?

@killercup I considered that, but unfortunately that would introduce a cyclic dependency.

@Nemo157 I instinctively tried that - placing the dependency in [dev-dependencies] - actually. Although I secretly hoped it didn't work as I really don't want to build/check contrib when working on core. A special doc-dependencies section would resolve this particular issue if it allowed cyclic dependencies.

Currently it's possible to link to fields of structs via a form of psuedo-path:

/// [`Foo::bar`]
pub struct Foo { pub bar: () }

Similarly it would be useful if it were possible to link to fields of a variant of an enum, probably via the same sort of psuedo-path:

/// [`Bar::Baz::quux`]
pub enum Bar {
    Baz { quux: () }
}

I'm trying to ascertain the readiness of this feature for release use, and was hoping the tracking issue might help. While it is working great for me on recent nightly builds, on the just released stable rust 1.30.0 (da5f414 2018-10-24), I'm surprised to find that the feature still isn't complete: some links using rust paths aren't resolved to html links.

Is this feature intended to be nightly only? Or is there flags or feature's to be set in order to use it with the rust stable channel? Should I file a new issue for the missed links on 1.30?

@Manishearth I'm not privy to how Rust tends to handle these sorts of things, so I realize this might go against established practice, but it seems to me like it would be useful to have what currently exists in stable, but just not promoted as something that actually works. It's not dangerous and is useful in its current state. I guess the risk is that nobody ever finishes the support for re-exports and we're stuck with a half working feature?

@Manishearth I've found it super useful in its current form, but I can see the worry around things being subtly broken.

I misinterpreted what I was initially seeing on 1.30: all, not just some, rust path style links (this feature) are not resolved on this latest (and prior) stable releases. Besides that not being explained anywhere I can find, the output is slightly confusing because the implicit type links are shown as obviously broken, e.g. "[SomeStruct]" where as links explicitly using a rust path are included verbatim, e.g. <a href="mod1::SomeStruct">, as would be expected without this feature being present.

As it seems now the intent for this was to be nightly-only until further along, shouldn't this tracking issue be edited to include the following, as it might save the next prospective user some confusion (@nrc)?

  • Stabilization PR (current implementation is nightly only, but not feature gated)

I agree that this is value add as found on current nightly. Authors can just avoid using the new link style for re-exports, as well as links to other crates which are not dependencies, until these are resolved.

But as it stands, its a bit difficult to justify using the feature when it will not generate correct links for a user-local cargo doc even on the latest stable, not to mention the minimum stable rust version. Please consider if keeping this off for stable, with no option or flag, is now more likely to cause trouble with broken links (as this starts getting used with nightly) vs any regressions stabilizing might otherwise cause, given that the new style link targets would never have worked without the feature?

Random idea: If it is planned to keep this semi-usable* for a few more months, it might interesting to see if we can make nightly rustdoc optionally print out the generated relative links (with obvious issues when docs are rendered in more than one place). I don't think we have precendence for this, but if you someone wants to have a fun project, they might even try to integrate this with rustfix (i.e., yield 'suggestion-like' output where you appen \n/// [$a]: $b for each link).

* You can use it right now, with the obvious missing features mentioned above. Since docs.rs uses a nightly compiler version where the intra doc links work, a lot of people reading the docs for your crate will see the correct links. People trying to build the docs will be disappointed, though.

@dekellum You are correct in your observations: The feature is currently written so that links will only ever resolve when running on a nightly (or locally-compiled) rustdoc. In any other situation, they will be passed through in the way you observed.

The current implementation of intra-doc links is not only incomplete, as @Manishearth mentioned, but is also bug-ridden: There have long been issues of spurious "dead links" being found in crates which aren't using intra-doc links at all. This, combined with the fact that we emit a proper warning lint for these resolution errors, led to situations where innocent crates suddenly broke because they had #![deny(warnings)] set.

To be honest, running this without a proper feature gate is shaky, in terms of how unstable features work in the rest of the compiler and rustdoc, but until recently i was uncertain if we could properly introduce a #![feature] gate to the compiler without a matching use in rustc itself. (All the #![feature] attributes that rustdoc currently uses are tied to attributes, so their check code is also in the compiler.) I would be willing to introduce a feature gate for this, since it would also stamp down on the false positive rate across the ecosystem.

However, it leads to similar broken-link situations. Imagine crate A which uses intra-doc links, complete with having the feature set. Now imagine crate B, which re-exports items from crate A to inline them into its own documentation, but doesn't use intra-doc links itself. Because crate B doesn't have the feature set, now those links from the crate A items won't resolve, even if they were accidentally correct because those items were re-exported into the right positions!

(This situation exists today; these crates are futures and hyper, though the items from futures that hyper re-exports don't have any links on them.)

I'm very hesitant to "partially stabilize" these links, because of the subtleties with re-exports that @Manishearth mentioned. It's too easy to use any part of this feature and get it to a position where it doesn't work. And the major hurdle for making that work requires a deep compiler refactor that is hard to find a champion for. If it comes down to it, if there is another Rust All-Hands early next year like the one in Berlin earlier this year, i may try getting the right people in a room together to design this problem out. However, that won't be able to happen for another few months.

I wonder if the standard library crates will need special treatment by this feature? For example, the link to the REPLACEMENT_CHAR constant in String docs needs to resolve to std::char::REPLACEMENT_CHAR or core::char::REPLACEMENT_CHAR depending if the String docs are being rendered as part of the libstd or liballoc docs.

Small bug report (let me know if this isn't the correct place to report this):

Currently, intra-doc links respect #[no_std] in a way that I'm not sure that they should. It means that simple links like [`String`] fail to link to the correct place in the standard library documentation.

Well, it's part of the issue indeed. I'll add a bullet point for it.

The problem is that we use the same resolver that the compiler has made - which means that it's affected by #![no_std]. To universally load in libstd for intra-doc links, we either need to totally ignore #![no_std] when building docs (which may cause erroneous compilation errors) or have some way to find and load libstd's information (and prelude, since you mentioned String specifically) after rustdoc takes over from the compiler. I'm sure it's possible, but it's more cumbersome than it seems.

It definitely seems like one of those cases where what is technically correct and what is expected are different, and we should design in favor of the latter.

The easiest stopgap solution I can see would be to add a new compiler attribute, doc, so that users can write:

#![cfg_attr(not(doc), no_std)]

Do I get it right that super, self, crate are not supported for path resolution?
super does not seem to throw a warning of failed resolution where the other two do. However it creates a link as: <a href="super::AsyncSeekExt::seek"><code>seek</code></a> which obviously doesn't work.

Ok, we do seem to have success with crate actually, but super is definitely broken.

I didn't see it mention in the RFC or this issue, but is there a way to link to a documentation header in another crate?

For example, I'd like to link to the syntax header of the regex crate. I can do something like this:

[see the regex syntax]: https://docs.rs/regex/latest/regex/#syntax

But not something like this (which would be preferred, as that would link to the correct version of the syntax documentation):

[see the regex syntax]: regex#syntax

No, intra doc links doesn't currently support linking to sections. It could. It doesn't support linking to other docs.rs either, but that's trickier to get right.

It doesn't support linking to other docs.rs either, but that's trickier to get right.

What do you mean by this? Quick testing shows that intra-rustdoc links works fine with locally available docs, or html_root_url specified in a dependency, or --extern-html-root-url overrides on the command line.

If you're referring to the fact there's no default external doc location when html_root_url isn't set, that is independent of the intra-rustdoc feature, it will presumably just work if html_root_url is updated to be able to default that.

What do you mean by this?

I mean the general case of linking to an external crate on docs.rs if it's not a dependency of yours or you are not on docs.rs.

Ok, so just #42301, which applies to all links to external documentation including intra-rustdoc links.

I filed #62830, in which the "module@" disambiguator does not seem to be respected.

I filed #62833 to propose support for linking to sections, [link text][MyStruct#my-section].

I filed #62834 about recognizing [`SomeType<T, U, V>`] as an intra doc link.

@dtolnay I'm not fan of such things... For instance, what should we do with [`Vec<String>`]? Only link to Vec and forget about String? I think it's better to remain "explicit" on what we want to be linked and not link to more than necessary. In your case, I think it should be "[`SomeType`]`<T, U, V>`".

@GuillaumeGomez I would be on board with linking if and only if the type parameters in the link match the names of the type's generic parameters, for example [`SomeType<T, U, V>`] would get linked but [`SomeType<String, String, ()>`] would not.

That'd force to check all the generic parameters, not very ideal...

Would you be able to clarify what is not ideal about checking generic parameters? Is it difficult to implement? If that's the only thing, would you consider accepting a PR that takes care of the implementation?

No it's not difficult. It's just not ideal because it creates additional rules that I find useless. The goal of a feature is to be simple enough to let users use it without having to wonder:

  1. Is it something I can do?
  2. What will be the outcome?

In this case, we can consider that the first point wouldn't be an issue but the second definitely would.

In your case, I think it should be [`SomeType`]`<T, U, V>`.

It would be great if there were better support for rendering this style of linking then, currently they have weird spacing, opened #62867 about it.

While this is unstable it should at least produce warnings with non-nightly compilers to avoid catching users off-guard: https://www.reddit.com/r/rust/comments/cm7rhv/hey_rustaceans_got_an_easy_question_ask_here/ew0nvo2/?context=1000

Opened as #63305

I see that #49459 has been merged to add support for primitive types, however I'm trying to link to slice::rotate_left and it doesn't seem to be supported. Looking at the state of the code since this PR, it seems like there's been a few changes, and that a more complete list of primitive types are represented. I tested pointer::is_null as well, to test if it's just polymorphic primitives, and it seems that might be the issue, since it also doesn't work as a intra-link.

Am I missing something?

@nixpulvis It's still very unstable and experimental. A lot remains to be done. Can you open an issue about it and cc me on it please?

Would it be possible to have this resolve links to trait implementation? For example:

/// To get an iterator on `MyStruct` call [MyStruct::into_iter].
/// The above generates a warning, "cannot be resolved ignoring".
/// Ideally it would generate a link to the method below.
pub struct MyStruct {
    // ...
}

impl IntoIterator for MyStruct {
    /// Specific documentation for my into_iter method.
    fn into_iter(self) -> Self::IntoIter {
        // ...
    }
}

@benkay86 Normally it should work as is. But like said above, it's still very experimental.

@benkay86 Normally it should work as is. But like said above, it's still very experimental.

It doesn't seem to work for me... on rustc 1.39.0-nightly (f0b58fcf0 2019-09-11)

@najamelan This comment isn't very helpful... Can you provide us details like the markdown, the context, etc... ?

Fully reproducible example:

/// To get an iterator on `MyStruct` call [MyStruct::go].
/// The above generates a warning, "cannot be resolved ignoring".
/// Ideally it would generate a link to the method below.
pub struct MyStruct {}


/// SomeTrait documentation
pub trait SomeTrait
{
    fn go();
}

impl SomeTrait for MyStruct {
    /// Specific documentation for my into_iter method.
    fn go() {}
}
$ rustdoc --version
rustdoc 1.39.0-nightly (6ef275e6c 2019-09-24)

$ cargo doc --no-deps --all-features
 Documenting doctrait v0.1.0 (/data/doc/code/doctrait)
warning: `[MyStruct::go]` cannot be resolved, ignoring it...
 --> src/lib.rs:1:44
  |
1 | /// To get an iterator on `MyStruct` call [MyStruct::go].
  |                                            ^^^^^^^^^^^^ cannot be resolved, ignoring
  |
  = note: `#[warn(intra_doc_link_resolution_failure)]` on by default
  = help: to escape `[` and `]` characters, just add '\' before them like `\[` or `\]`

    Finished dev [unoptimized + debuginfo] target(s) in 0.90s

So, trait methods aren't working. Thanks for the feedback. Adding it to the points.

I filed #65354 to have the disambiguator stripped from the link text in something like
/// [`trait@Name`].

Is there any centralised documentation for how exactly this feature works, considering how it's changed so much from the initial RFC? Like, specifically document what kinds of links should work and which shouldn't, and if parts of the text are removed from the final result?

Like, I get that it's not going to be stabilised any time soon, but many folks use this feature and it's hard to reason which things work and which don't at the moment.

The only thing blocking stabilization now is #65983

Isn't documentation also a blocker? 😅

That's usually a part of stabilization.

Is there any way to hide any crate:: prefix when linking to types not within the current module and not in scope when using shorthand links?

I notice in some of my documentation, I have some types imported, and can link to them using [`MyStruct`] but when I want to link to some other type that is not imported because it isn't actively used in the module, I have to use [`crate::OtherStruct`], which looks a bit off in the documentation.

I realise I can name the link whatever I want and then turn it into an actual link later by adding a reference, but that increases the bloat of the comment in the code itself.

I guess it's a minor thing, and there's no clean way to solve this that isn't probably at least somewhat surprising to some, but I wanted to mention it nonetheless.

@JeanMertz Have you tried something like:

[`OtherStruct`][crate::OtherStruct]

One way that I tend to do this is using the (currently unstable, but pending stabilisation in #61351) #[cfg(rustdoc)].

i.e., at the top of the file, I put:

#[cfg(rustdoc)]
use crate::OtherStruct;

and then you can reference the type without the crate:: prefix.

You can do this without the #[cfg(rustdoc)] by simply adding #[allow(unused)], although I prefer the former as it will still lint if you don't actually use the import.

Could someone in the know please file an issue around "Handle fields inside struct variants" so that I know what might be needed in order to close off that blocker?

It's not a blocker. It's about giving the ability to link directly to a struct field by naming it MyStruct::field in the link.

It's about giving the ability to link directly to a struct field by naming it MyStruct::field in the link.

Isn't that ambiguous when there is also an inherent method with the same name? It's also "syntactically unnatural" to refer to struct fields in this way, though I can't think of a better way right now.