diff --git a/src/librustdoc/html/render/search_index.rs b/src/librustdoc/html/render/search_index.rs
index 5f69cc030e266..935fe258c8076 100644
--- a/src/librustdoc/html/render/search_index.rs
+++ b/src/librustdoc/html/render/search_index.rs
@@ -13,6 +13,7 @@ use ::serde::{Deserialize, Serialize};
use rustc_ast::join_path_syms;
use rustc_data_structures::fx::{FxHashMap, FxHashSet, FxIndexMap};
use rustc_data_structures::thin_vec::ThinVec;
+use rustc_hir::def_id::LOCAL_CRATE;
use rustc_hir::find_attr;
use rustc_middle::ty::TyCtxt;
use rustc_span::def_id::DefId;
@@ -22,7 +23,7 @@ use stringdex::internals as stringdex_internals;
use tracing::instrument;
use crate::clean::types::{Function, Generics, ItemId, Type, WherePredicate};
-use crate::clean::{self, utils};
+use crate::clean::{self, ExternalLocation, utils};
use crate::config::ShouldMerge;
use crate::error::Error;
use crate::formats::cache::{Cache, OrphanImplItem};
@@ -616,7 +617,8 @@ impl SerializedSearchIndex {
trait_parent,
deprecated,
unstable,
- associated_item_disambiguator,
+ associated_item_disambiguator_or_extern_crate_url:
+ associated_item_disambiguator,
}| EntryData {
krate: *map.get(krate).unwrap(),
ty: *ty,
@@ -627,7 +629,8 @@ impl SerializedSearchIndex {
trait_parent: trait_parent.and_then(|path_id| map.get(&path_id).copied()),
deprecated: *deprecated,
unstable: *unstable,
- associated_item_disambiguator: associated_item_disambiguator.clone(),
+ associated_item_disambiguator_or_extern_crate_url:
+ associated_item_disambiguator.clone(),
},
),
self.descs[id].clone(),
@@ -898,7 +901,7 @@ struct EntryData {
trait_parent: Option,
deprecated: bool,
unstable: bool,
- associated_item_disambiguator: Option,
+ associated_item_disambiguator_or_extern_crate_url: Option,
}
impl Serialize for EntryData {
@@ -915,7 +918,7 @@ impl Serialize for EntryData {
seq.serialize_element(&self.trait_parent.map(|id| id + 1).unwrap_or(0))?;
seq.serialize_element(&if self.deprecated { 1 } else { 0 })?;
seq.serialize_element(&if self.unstable { 1 } else { 0 })?;
- if let Some(disambig) = &self.associated_item_disambiguator {
+ if let Some(disambig) = &self.associated_item_disambiguator_or_extern_crate_url {
seq.serialize_element(&disambig)?;
}
seq.end()
@@ -961,7 +964,8 @@ impl<'de> Deserialize<'de> for EntryData {
trait_parent: Option::::from(trait_parent).map(|path| path as usize),
deprecated: deprecated != 0,
unstable: unstable != 0,
- associated_item_disambiguator,
+ associated_item_disambiguator_or_extern_crate_url:
+ associated_item_disambiguator,
})
}
}
@@ -1389,7 +1393,7 @@ pub(crate) fn build_index(
trait_parent: None,
deprecated: false,
unstable: false,
- associated_item_disambiguator: None,
+ associated_item_disambiguator_or_extern_crate_url: None,
}),
crate_doc,
None,
@@ -1528,7 +1532,8 @@ pub(crate) fn build_index(
exact_module_path,
deprecated: item.is_deprecated,
unstable: item.is_unstable,
- associated_item_disambiguator: if let Some(impl_id) = item.impl_id
+ associated_item_disambiguator_or_extern_crate_url: if let Some(impl_id) =
+ item.impl_id
&& let Some(parent_idx) = item.parent_idx
&& associated_item_duplicates
.get(&(parent_idx, item.ty, item.name))
@@ -1537,6 +1542,14 @@ pub(crate) fn build_index(
> 1
{
Some(render::get_id_for_impl(tcx, ItemId::DefId(impl_id)))
+ } else if item.ty == ItemType::ExternCrate
+ && let Some(local_def_id) = item.defid.and_then(|def_id| def_id.as_local())
+ && let cnum = tcx.extern_mod_stmt_cnum(local_def_id).unwrap_or(LOCAL_CRATE)
+ && let Some(ExternalLocation::Remote { url, is_absolute }) =
+ cache.extern_locations.get(&cnum)
+ && *is_absolute
+ {
+ Some(format!("{}{}", url, tcx.crate_name(cnum).as_str()))
} else {
None
},
diff --git a/src/librustdoc/html/static/js/rustdoc.d.ts b/src/librustdoc/html/static/js/rustdoc.d.ts
index 3857e6e9fd878..2d2baf22e0f63 100644
--- a/src/librustdoc/html/static/js/rustdoc.d.ts
+++ b/src/librustdoc/html/static/js/rustdoc.d.ts
@@ -244,7 +244,7 @@ declare namespace rustdoc {
traitParent: number?,
deprecated: boolean,
unstable: boolean,
- associatedItemDisambiguator: string?,
+ associatedItemDisambiguatorOrExternCrateUrl: string?,
}
/**
diff --git a/src/librustdoc/html/static/js/search.js b/src/librustdoc/html/static/js/search.js
index e540b3942d249..85a76054a12dd 100644
--- a/src/librustdoc/html/static/js/search.js
+++ b/src/librustdoc/html/static/js/search.js
@@ -1657,7 +1657,7 @@ class DocSearch {
traitParent: raw[5] === 0 ? null : raw[5] - 1,
deprecated: raw[6] === 1 ? true : false,
unstable: raw[7] === 1 ? true : false,
- associatedItemDisambiguator: raw.length === 8 ? null : raw[8],
+ associatedItemDisambiguatorOrExternCrateUrl: raw.length === 8 ? null : raw[8],
};
}
@@ -2176,7 +2176,12 @@ class DocSearch {
"/" + type + "." + name + ".html";
} else if (type === "externcrate") {
displayPath = "";
- href = this.rootPath + name + "/index.html";
+ let base = this.rootPath + name;
+ if (item.entry && item.entry.associatedItemDisambiguatorOrExternCrateUrl) {
+ base = item.entry.associatedItemDisambiguatorOrExternCrateUrl;
+ }
+
+ href = base + "/index.html";
} else if (item.parent) {
const myparent = item.parent;
let anchor = type + "." + name;
@@ -2201,8 +2206,8 @@ class DocSearch {
} else {
displayPath = path + "::" + myparent.name + "::";
}
- if (item.entry && item.entry.associatedItemDisambiguator !== null) {
- anchor = item.entry.associatedItemDisambiguator + "/" + anchor;
+ if (item.entry && item.entry.associatedItemDisambiguatorOrExternCrateUrl !== null) {
+ anchor = item.entry.associatedItemDisambiguatorOrExternCrateUrl + "/" + anchor;
}
href = this.rootPath + path.replace(/::/g, "/") +
"/" + pageType +
diff --git a/src/tools/rustdoc-js/tester.js b/src/tools/rustdoc-js/tester.js
index 124839efdd029..aaf40ceeadf71 100644
--- a/src/tools/rustdoc-js/tester.js
+++ b/src/tools/rustdoc-js/tester.js
@@ -4,6 +4,8 @@ const fs = require("fs");
const path = require("path");
const { isGeneratorObject } = require("util/types");
+const CHANNEL_REGEX = new RegExp("/nightly/|/beta/|/stable/|/1\\.[0-9]+\\.[0-9]+/");
+
function arrayToCode(array) {
return array.map((value, index) => {
value = value.split(" ").join(" ");
@@ -56,6 +58,8 @@ function valueMapper(key, testOutput) {
value = testOutput["parent"]["name"];
}
}
+ } else if (key === "href") {
+ value = value.replace(CHANNEL_REGEX, "/$CHANNEL/");
}
return value;
}
@@ -69,13 +73,14 @@ function betterLookingDiff(expected, testOutput) {
if (!Object.prototype.hasOwnProperty.call(expected, key)) {
continue;
}
+ const expectedValue = expected[key];
if (!testOutput || !Object.prototype.hasOwnProperty.call(testOutput, key)) {
- output += "-" + spaces + contentToDiffLine(key, expected[key]) + "\n";
+ output += "-" + spaces + contentToDiffLine(key, expectedValue) + "\n";
continue;
}
const value = valueMapper(key, testOutput);
- if (value !== expected[key]) {
- output += "-" + spaces + contentToDiffLine(key, expected[key]) + "\n";
+ if (value !== expectedValue) {
+ output += "-" + spaces + contentToDiffLine(key, expectedValue) + "\n";
output += "+" + spaces + contentToDiffLine(key, value) + "\n";
} else {
output += spaces + " " + contentToDiffLine(key, value) + "\n";
@@ -92,7 +97,11 @@ function lookForEntry(expected, testOutput) {
continue;
}
const value = valueMapper(key, testOutputEntry);
- if (value !== expected[key]) {
+ let expectedValue = expected[key];
+ if (key === "href") {
+ expectedValue = expectedValue.replace(CHANNEL_REGEX, "/$CHANNEL/");
+ }
+ if (value !== expectedValue) {
allGood = false;
break;
}
diff --git a/tests/rustdoc-js/import-filter.js b/tests/rustdoc-js/import-filter.js
index 408d0e3326179..6237ddb837974 100644
--- a/tests/rustdoc-js/import-filter.js
+++ b/tests/rustdoc-js/import-filter.js
@@ -6,15 +6,21 @@ const EXPECTED = [
'query': 'import:st',
'others': [
{ 'path': 'foo', 'name': 'st', 'href': '../foo/index.html#reexport.st' },
- // FIXME: `href` is wrong:
- { 'path': 'foo', 'name': 'st2', 'href': '../st2/index.html' },
+ {
+ 'path': 'foo',
+ 'name': 'st2',
+ 'href': 'https://doc.rust-lang.org/$CHANNEL/std/index.html'
+ },
],
},
{
'query': 'externcrate:st',
'others': [
- // FIXME: `href` is wrong:
- { 'path': 'foo', 'name': 'st2', 'href': '../st2/index.html' },
+ {
+ 'path': 'foo',
+ 'name': 'st2',
+ 'href': 'https://doc.rust-lang.org/$CHANNEL/std/index.html'
+ },
],
},
];
diff --git a/tests/rustdoc-js/renamed-crate-148300.js b/tests/rustdoc-js/renamed-crate-148300.js
new file mode 100644
index 0000000000000..fa3d64e77c5fd
--- /dev/null
+++ b/tests/rustdoc-js/renamed-crate-148300.js
@@ -0,0 +1,12 @@
+// Regression test for
+//
+// This ensures that extern crates in search results link to the correct url.
+
+const EXPECTED = [
+ {
+ query: 'st2',
+ others: [
+ { name: 'st2', href: 'https://doc.rust-lang.org/$CHANNEL/std/index.html' }
+ ],
+ },
+];
diff --git a/tests/rustdoc-js/renamed-crate-148300.rs b/tests/rustdoc-js/renamed-crate-148300.rs
new file mode 100644
index 0000000000000..9bb54582e2f10
--- /dev/null
+++ b/tests/rustdoc-js/renamed-crate-148300.rs
@@ -0,0 +1 @@
+pub extern crate std as st2;