Repository: rust-lang/rust-analyzer (rust)

Issue:
Incorrect doc links in `src/cmp.rs` lead to invalid hyperlinks in `rust-analyzer`
<!--
Troubleshooting guide: https://rust-analyzer.github.io/manual.html#troubleshooting
Forum for questions: https://users.rust-lang.org/c/ide/14

Before submitting, please make sure that you're not running into one of these known issues:

 1. on-the-fly diagnostics are mostly unimplemented (`cargo check` diagnostics will be shown when saving a file): #3107

Otherwise please try to provide information which will help us to fix the issue faster. Minimal reproducible examples with few dependencies are especially lovely <3.
-->

Original post as [Issue 117666](https://github.com/rust-lang/rust/issues/117666) in `rust-lang/rust/` repo.


## Errors in `rust` source code

- [`PartialEq#derivable`](https://github.com/rust-lang/rust/blob/4e0fb98a5c41be1227845219b81ab147708fd99a/library/core/src/cmp.rs#L245) should be `trait.PartialEq#derivable`
- [`Ord#derivable`](https://github.com/rust-lang/rust/blob/4e0fb98a5c41be1227845219b81ab147708fd99a/library/core/src/cmp.rs#L888) should be `trait.Ord#derivable`
- [`PartialOrd`](https://github.com/rust-lang/rust/blob/4e0fb98a5c41be1227845219b81ab147708fd99a/library/core/src/cmp.rs#L1178) should be `trait.PartialOrd#derivable`

## Links to invalid hyperlinks in `https://doc.rust-lang.org/`

- `PartialEq`: https://doc.rust-lang.org/nightly/src/core/cmp.rs.html#245
- `Ord`: https://doc.rust-lang.org/nightly/src/core/cmp.rs.html#888
- `PartialOrd`: https://doc.rust-lang.org/nightly/src/core/cmp.rs.html#1178

## Incorrect links in `rust-analyzer` provides a link to a nonexistent website

![screenshot of `rust-analyzer` providing an invalid link](https://github.com/rust-lang/rust-analyzer/assets/14944402/106c6d73-ff38-4f37-8df2-7c6485fb27e4)

- https://doc.rust-lang.org/nightly/core/cmp/PartialEq#derivable (incorrect)
- https://doc.rust-lang.org/nightly/core/cmp/trait.PartialEq.html#derivable (correct)
  - has `trait` before `.PartialEq`

## Related Issues in `rust-analyzer`
- https://github.com/rust-lang/rust-analyzer/issues/15835

**IDE**: from `VSCode > Help: About`

```
Version: 1.84.1 (user setup)
Commit: 2b35e1e6d88f1ce073683991d1eff5284a32690f
Date: 2023-11-06T12:35:59.022Z
Electron: 25.9.2
ElectronBuildId: 24603566
Chromium: 114.0.5735.289
Node.js: 18.15.0
V8: 11.4.183.29-electron.0
OS: Windows_NT x64 10.0.22621
```

**rust-analyzer version**: (eg. output of "rust-analyzer: Show RA Version" command, accessible in VSCode via <kbd>Ctrl/⌘</kbd>+<kbd>Shift</kbd>+<kbd>P</kbd>)
- `rust-analyzer version: 0.3.1722-standalone`

**rustc version**: (eg. output of `rustc -V`)
- `rustc 1.75.0-nightly (fee5518cd 2023-11-05)`

**relevant settings**: (eg. client settings, or environment variables like `CARGO`, `RUSTC`, `RUSTUP_HOME` or `CARGO_HOME`)
- no adjustments made that I am aware of. happy to provide if they are deemed relevant!

Pull request:
Fix intra doc links
Fixes #15848 

Patch:
diff --git a/crates/ide/src/doc_links.rs b/crates/ide/src/doc_links.rs
index 4b0ecb9cf9..1357f42636 100644
--- a/crates/ide/src/doc_links.rs
+++ b/crates/ide/src/doc_links.rs
@@ -64,13 +64,12 @@ pub(crate) fn rewrite_links(db: &RootDatabase, markdown: &str, definition: Defin
             // * path-based links: `../../module/struct.MyStruct.html`
             // * module-based links (AKA intra-doc links): `super::super::module::MyStruct`
             if let Some((target, title)) = rewrite_intra_doc_link(db, definition, target, title) {
-                return (None, target, title);
-            }
-            if let Some(target) = rewrite_url_link(db, definition, target) {
-                return (Some(LinkType::Inline), target, title.to_string());
+                (None, target, title)
+            } else if let Some(target) = rewrite_url_link(db, definition, target) {
+                (Some(LinkType::Inline), target, title.to_string())
+            } else {
+                (None, target.to_string(), title.to_string())
             }
-
-            (None, target.to_string(), title.to_string())
         }
     });
     let mut out = String::new();
@@ -369,16 +368,21 @@ fn rewrite_intra_doc_link(
 ) -> Option<(String, String)> {
     let (link, ns) = parse_intra_doc_link(target);
 
+    let (link, anchor) = match link.split_once('#') {
+        Some((new_link, anchor)) => (new_link, Some(anchor)),
+        None => (link, None),
+    };
+
     let resolved = resolve_doc_path_for_def(db, def, link, ns)?;
     let mut url = get_doc_base_urls(db, resolved, None, None).0?;
 
-    let (_, file, frag) = filename_and_frag_for_def(db, resolved)?;
+    let (_, file, _) = filename_and_frag_for_def(db, resolved)?;
     if let Some(path) = mod_path_of_def(db, resolved) {
         url = url.join(&path).ok()?;
     }
 
     url = url.join(&file).ok()?;
-    url.set_fragment(frag.as_deref());
+    url.set_fragment(anchor);
 
     Some((url.into(), strip_prefixes_suffixes(title).to_string()))
 }


Test patch:
diff --git a/crates/ide/src/doc_links/tests.rs b/crates/ide/src/doc_links/tests.rs
index e1f5ccc228..37da3d6514 100644
--- a/crates/ide/src/doc_links/tests.rs
+++ b/crates/ide/src/doc_links/tests.rs
@@ -664,3 +664,29 @@ pub struct $0Foo;
         expect![["[`foo`]"]],
     );
 }
+
+#[test]
+fn rewrite_intra_doc_link() {
+    check_rewrite(
+        r#"
+        //- minicore: eq, derive
+        //- /main.rs crate:foo
+        //! $0[PartialEq]
+        fn main() {}
+        "#,
+        expect!["[PartialEq](https://doc.rust-lang.org/stable/core/cmp/trait.PartialEq.html)"],
+    );
+}
+
+#[test]
+fn rewrite_intra_doc_link_with_anchor() {
+    check_rewrite(
+        r#"
+        //- minicore: eq, derive
+        //- /main.rs crate:foo
+        //! $0[PartialEq#derivable]
+        fn main() {}
+        "#,
+        expect!["[PartialEq#derivable](https://doc.rust-lang.org/stable/core/cmp/trait.PartialEq.html#derivable)"],
+    );
+}