Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[intra-doc-links] primitives inconsistently point offline or online in std docs #79630

Closed
jnqnfe opened this issue Dec 2, 2020 · 6 comments · Fixed by #87073
Closed

[intra-doc-links] primitives inconsistently point offline or online in std docs #79630

jnqnfe opened this issue Dec 2, 2020 · 6 comments · Fixed by #87073
Assignees
Labels
A-intra-doc-links Area: Intra-doc links, the ability to link to items in docs by name T-rustdoc Relevant to the rustdoc team, which will review and decide on the PR/issue.

Comments

@jnqnfe
Copy link
Contributor

jnqnfe commented Dec 2, 2020

I noticed with an offline copy of the Rust 1.47 docs from the package in the Debian Sid repo, that there is an undesirable inconsistency in where links to primitives point to within the std documentation.

Take for instance the documentation for the std::ffi::CStr::to_string_lossy() method, which I just happened to notice it on. All links except the one for the str type point to the offline pages, while the one for str points to the online nightly page. However contrastingly the other link to str just above that one, in the return type, points to the offline page. Essentially all links to std types point offline, except those to primatives, which point online when within the doc comments but offline if in the type/function description.

It seems that the handling of primatives for intra-doc-link processing makes an assumption that links should always point to the online page, without making any exception for the std crate itself, unlike what is done otherwise.

@jnqnfe jnqnfe changed the title [intra-doc-links] primative inconsistently point offline or online in std docs [intra-doc-links] primatives inconsistently point offline or online in std docs Dec 2, 2020
@jonas-schievink jonas-schievink added A-intra-doc-links Area: Intra-doc links, the ability to link to items in docs by name T-rustdoc Relevant to the rustdoc team, which will review and decide on the PR/issue. labels Dec 2, 2020
@championshuttler
Copy link

@jonas-schievink, I would like to contribute to this issue , can you please help me getting started? Thanks

@jyn514 jyn514 changed the title [intra-doc-links] primatives inconsistently point offline or online in std docs [intra-doc-links] primitives inconsistently point offline or online in std docs Dec 2, 2020
@jyn514
Copy link
Member

jyn514 commented Dec 2, 2020

I double checked and this issue is still present on rustc 1.50.0-nightly (349b3b324 2020-11-29). The relevant code is

let url = match cache.extern_locations.get(krate) {
Some(&(_, _, ExternalLocation::Local)) => {
let depth = CURRENT_DEPTH.with(|l| l.get());
"../".repeat(depth)
}
Some(&(_, _, ExternalLocation::Remote(ref s))) => s.to_string(),
Some(&(_, _, ExternalLocation::Unknown)) | None => String::from(
// NOTE: intentionally doesn't pass crate name to avoid having
// different primitive links between crates
if UnstableFeatures::from_environment(None).is_nightly_build() {
"https://doc.rust-lang.org/nightly"
} else {
"https://doc.rust-lang.org"
},
),
};

I tried unsuccessfully to debug what's going on here in #74077. @championshuttler feel free to pick back up on that if you're interested.

@ijackson
Copy link
Contributor

I just stumbled across this. It's weird. In my local docs install the primitive links in the "crate level docs" for std, and in the "all items", are right and point to the local copy of std's docs. It seems this only triggers with an explicit primitive@?

The code in #79630 (comment) is strange. Why would it ever be right to hardcode the url deep in the code like that?

I think maybe this is here so that individual online docs links in places like docs.rs point to docs.rust-lang.org but why not do whatever is done to make things like std::result::Result work properly?

@jyn514
Copy link
Member

jyn514 commented Feb 21, 2021

I think maybe this is here so that individual online docs links in places like docs.rs point to docs.rust-lang.org but why not do whatever is done to make things like std::result::Result work properly?

I don't know. The same check for cache.external_locations is done for both primitives and Result, the hardcoded URL is just a fallback. Fixing this requires figuring out why external_locations isn't being hit only for primitives.

bors added a commit to rust-lang-ci/rust that referenced this issue Mar 24, 2021
…lnay

Expose str::SplitInclusive in alloc and therefore in std

This seems to have been omitted from the beginning when this feature was first introduced in 86bf962.  Most users won't need to name this type which is probably why this wasn't noticed in the meantime.

See rust-lang#83372 for a different but related bug.

### Notes for reviewers

I think I have got this right but TBH I am not very familiar with the relationship between core and std and so on.  <strike>I also haven't don't any kind of test (not even a build) yet.  I will do a local docs build to see that the type now appears in the std docs.</strike>  I did a local docs build and it has made this type appear as `std::str::SplitInclusive` as expected

The linkification of the return value from `str::split_inclusive` teleports me to the online url for `core::str::SplitInclusive`.  I think this may be a rustdoc anomaly (similar to rust-lang#79630 maybe) but I am not sure.  Perhaps it means I haven't done the `std` -> `core` referrence correctly.

I made this insta-stable since it seems like simply a bug.  Please LMK if that is not right.  *(edited to add:)* In particular, IDK how this ought to relate to the (?)current release process.
@jyn514 jyn514 added the E-help-wanted Call for participation: Help is requested to fix this issue. label Jul 2, 2021
@jyn514
Copy link
Member

jyn514 commented Jul 6, 2021

I found the issue. links() explicitly skips the check to href() for primitives, which only leaves hard-coded URLs as the fallback:

// FIXME(83083): using fragments as a side-channel for
// primitive names is very unfortunate
None => {
let relative_to = &cx.current;
if let Some(ref fragment) = *fragment {
let url = match cx.cache().extern_locations.get(&self.def_id.krate()) {

In particular, the check for extern_locations.get(&self.def_id.krate) is completely bogus, that's the crate of the item being documented, not the primitive. I have some partial work on fixing it locally, see https://rust-lang.zulipchat.com/#narrow/stream/266220-rustdoc/topic/doc.28primitive.29

@jyn514
Copy link
Member

jyn514 commented Jul 6, 2021

In particular, the check for extern_locations.get(&self.def_id.krate) is completely bogus, that's the crate of the item being documented, not the primitive

... which will never be in extern_locations because it's always the current crate 🤦

@jyn514 jyn514 self-assigned this Jul 11, 2021
@jyn514 jyn514 removed the E-help-wanted Call for participation: Help is requested to fix this issue. label Jul 11, 2021
GuillaumeGomez added a commit to GuillaumeGomez/rust that referenced this issue Aug 26, 2021
…omez

Fix rustdoc handling of primitive items

This is a complicated PR and does a lot of things. I'm willing to split it up a little more if it would help reviewing, but it would be tricky and I'd rather not unless it's necessary.

 ## What does this do?

- Fixes rust-lang#73423.
- Fixes rust-lang#79630. I'm not sure how to test this for the standard library explicitly, but you can see from some of the diffs from the `no_std` tests. I also tested it locally and it works correctly: ![image](https://user-images.githubusercontent.com/23638587/125214383-e1fdd000-e284-11eb-8048-76b5df958aad.png)
- Fixes rust-lang#83083.

## Why are these changes interconnected?

- Allowing anchors (rust-lang#83083) without fixing the online/offline problem (rust-lang#79630) will actually just silently discard the anchors, that's not a fix. The online/offline problem is directly related to the fragment hack; links need to go through `fn href()` to be fixed.
- Technically I could fix the online/offline problem without removing the error on anchors; I am willing to separate that out if it would be helpful for reviewing. However I can't fix the anchor problem without adding docs to core, since rustdoc needs all those primitives to have docs to avoid a fallback, and currently `#![no_std]` crates don't have docs for primitives. I also can't fix the online/offline problem without removing the fragment hack, since otherwise diffs like this will be wrong for some primitives but not others:
```diff
`@@` -385,7 +381,7 `@@` fn resolve_primitive_associated_item(
                         ty::AssocKind::Const => "associatedconstant",
                         ty::AssocKind::Type => "associatedtype",
                     };
-                    let fragment = format!("{}#{}.{}", prim_ty.as_sym(), out, item_name);
+                    let fragment = format!("{}.{}", out, item_name);
                     (Res::Primitive(prim_ty), fragment, Some((kind.as_def_kind(), item.def_id)))
                 })
         })
```
- Adding primitive docs to core without making any other change will cause links to go to `core` instead of `std`, even for crates with `extern crate std`. See "Breaking changes to doc(primitive)" below for why this is the case. That said, I could add some special casing to rustdoc at the same time that would let me separate this change from the others (it would fix rust-lang#73423 but still special-case intra-doc links). I'm willing to separate that out if helpful for reviewing.

### Add primitive documentation to libcore

This works by reusing the same `include!("primitive_docs.rs")` file in both core and std, and then special-casing links in core to use relative links instead of intra-doc links. This doesn't use purely intra-doc links because some of the primitive docs links to items only in std; this doesn't use purely relative links because that introduces new broken links when the docs are re-exported (e.g. String's `&str` deref impl, or Vec's slice deref impl).

### Fix inconsistent online/offline primitive docs

This does four things:
- Records modules with `doc(primitive)` in `cache.external_paths`. This is necessary for `href()` to find them later.
- Makes `cache.primitive_locations` available to the intra-doc link pass, by refactoring out a `PrimitiveType::primitive_locations` function that only uses `TyCtxt`.
- Special cases modules with `doc(primitive)` to be treated as always public for the purpose of links.
- Removes the fragment hack. cc `@notriddle,` I know you added some comments about this in the code (thank you for that!)

### Breaking changes to `doc(primitive)`

"Breaking" is a little misleading here - these are changes in behavior, none of them will cause code to fail to compile.

Let me preface this by saying I think stabilizing `doc(primitive)` was a uniquely terrible idea. As far as I can tell, it was stabilized by oversight; it's been stable since 1.0. No one should have need to use it except the standard library, and a crater run shows that in fact no one is using it: rust-lang#87050 (comment). I hope to actually make `doc(primitive)` a no-op unless you opt-in with a nightly feature, which will keep crates compiling without forcing rustdoc into trying to keep somewhat arbitrary behavior guarantees; but for now, this just subtly changes some of the behavior if you use `doc(primitive)` in a dependency.

That said, here are the changes:
-  Refactoring out `primitive_locations()` is technically a change in behavior, since it no longer looks for primitives in crates that were passed through `--extern`, but not used by the crate; however, that seems like such an unlikely edge case it's not worth dealing with.
- The precedence given to primitive locations is no longer just arbitrary, it can also be inconsistent from run to run. Let me explain that more: previously, primitive locations were sorted by the `CrateNum`; the comment on that sort said "Favor linking to as local extern as possible, so iterate all crates in reverse topological order." Unfortunately, that's not actually what CrateNum tracks: it measures the order crates are loaded, not the number of intermediate crates between that dependency and the root crate. It happened to work as intended before because the compiler injects `extern crate std;` at the top of every crate, which ensured it would have the first CrateNum other than the current, but every other CrateNum was completely arbitrary (for example, `core` often had a later CrateNum than `std`). This now removes the sort on CrateNum completely and special-cases core instead. In particular, if you depend on both `std` and a crate which defines a `doc(primitive)` module, it's arbitrary whether rustdoc will use the docs from std or the ones from the other crate. cc `@alexcrichton,` you wrote this originally.

cc `@rust-lang/rustdoc`
cc `@rust-lang/libs` for the addition to `core` (the commit you're interested in is rust-lang@36729b0)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
A-intra-doc-links Area: Intra-doc links, the ability to link to items in docs by name T-rustdoc Relevant to the rustdoc team, which will review and decide on the PR/issue.
Projects
None yet
Development

Successfully merging a pull request may close this issue.

6 participants