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;