diff --git a/compiler/rustc_metadata/src/creader.rs b/compiler/rustc_metadata/src/creader.rs index e65c7a684260d..ddf6da047e33e 100644 --- a/compiler/rustc_metadata/src/creader.rs +++ b/compiler/rustc_metadata/src/creader.rs @@ -12,6 +12,7 @@ use rustc_data_structures::fx::FxHashSet; use rustc_data_structures::owned_slice::OwnedSlice; use rustc_data_structures::svh::Svh; use rustc_data_structures::sync::{self, FreezeReadGuard, FreezeWriteGuard}; +use rustc_data_structures::unord::UnordMap; use rustc_errors::DiagCtxtHandle; use rustc_expand::base::SyntaxExtension; use rustc_fs_util::try_canonicalize; @@ -68,6 +69,9 @@ pub struct CStore { /// This crate has a `#[alloc_error_handler]` item. has_alloc_error_handler: bool, + /// Names that were used to load the crates via `extern crate` or paths. + resolved_externs: UnordMap, + /// Unused externs of the crate unused_externs: Vec, } @@ -268,6 +272,22 @@ impl CStore { self.metas[cnum] = Some(Box::new(data)); } + /// Save the name used to resolve the extern crate in the local crate + /// + /// The name isn't always the crate's own name, because `sess.opts.externs` can assign it another name. + /// It's also not always the same as the `DefId`'s symbol due to renames `extern crate resolved_name as defid_name`. + pub(crate) fn set_resolved_extern_crate_name(&mut self, name: Symbol, extern_crate: CrateNum) { + self.resolved_externs.insert(name, extern_crate); + } + + /// Crate resolved and loaded via the given extern name + /// (corresponds to names in `sess.opts.externs`) + /// + /// May be `None` if the crate wasn't used + pub fn resolved_extern_crate(&self, externs_name: Symbol) -> Option { + self.resolved_externs.get(&externs_name).copied() + } + pub(crate) fn iter_crate_data(&self) -> impl Iterator { self.metas .iter_enumerated() @@ -494,6 +514,7 @@ impl CStore { alloc_error_handler_kind: None, has_global_allocator: false, has_alloc_error_handler: false, + resolved_externs: UnordMap::default(), unused_externs: Vec::new(), } } @@ -533,7 +554,7 @@ impl<'a, 'tcx> CrateLoader<'a, 'tcx> { // We're also sure to compare *paths*, not actual byte slices. The // `source` stores paths which are normalized which may be different // from the strings on the command line. - let source = self.cstore.get_crate_data(cnum).cdata.source(); + let source = data.source(); if let Some(entry) = self.sess.opts.externs.get(name.as_str()) { // Only use `--extern crate_name=path` here, not `--extern crate_name`. if let Some(mut files) = entry.files() { @@ -1302,6 +1323,7 @@ impl<'a, 'tcx> CrateLoader<'a, 'tcx> { let path_len = definitions.def_path(def_id).data.len(); self.cstore.update_extern_crate( cnum, + name, ExternCrate { src: ExternCrateSource::Extern(def_id.to_def_id()), span: item.span, @@ -1320,6 +1342,7 @@ impl<'a, 'tcx> CrateLoader<'a, 'tcx> { self.cstore.update_extern_crate( cnum, + name, ExternCrate { src: ExternCrateSource::Path, span, diff --git a/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs b/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs index 6943d4198df43..a581eec8e6e33 100644 --- a/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs +++ b/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs @@ -630,14 +630,29 @@ impl CStore { } } - pub(crate) fn update_extern_crate(&mut self, cnum: CrateNum, extern_crate: ExternCrate) { + /// Track how an extern crate has been loaded + /// + /// * the `name` is for [`Self::set_resolved_extern_crate_name`] + /// * `extern_crate` is for diagnostics + pub(crate) fn update_extern_crate( + &mut self, + cnum: CrateNum, + name: Symbol, + extern_crate: ExternCrate, + ) { + debug_assert_eq!(extern_crate.dependency_of, LOCAL_CRATE); + self.set_resolved_extern_crate_name(name, cnum); + self.update_extern_crate_inner(cnum, extern_crate); + } + + fn update_extern_crate_inner(&mut self, cnum: CrateNum, extern_crate: ExternCrate) { let cmeta = self.get_crate_data_mut(cnum); if cmeta.update_extern_crate(extern_crate) { // Propagate the extern crate info to dependencies if it was updated. let extern_crate = ExternCrate { dependency_of: cnum, ..extern_crate }; let dependencies = mem::take(&mut cmeta.dependencies); for &dep_cnum in &dependencies { - self.update_extern_crate(dep_cnum, extern_crate); + self.update_extern_crate_inner(dep_cnum, extern_crate); } self.get_crate_data_mut(cnum).dependencies = dependencies; } diff --git a/src/doc/rustdoc/src/unstable-features.md b/src/doc/rustdoc/src/unstable-features.md index 27910ad0ab796..dbfebec017609 100644 --- a/src/doc/rustdoc/src/unstable-features.md +++ b/src/doc/rustdoc/src/unstable-features.md @@ -395,6 +395,11 @@ flags to control that behavior. When the `--extern-html-root-url` flag is given one of your dependencies, rustdoc use that URL for those docs. Keep in mind that if those docs exist in the output directory, those local docs will still override this flag. +Crate names in this flag refer to the names in the [extern prelude], and support crates renamed +via the `--extern` `rustc` flag. + +[extern prelude]: https://doc.rust-lang.org/reference/names/preludes.html#extern-prelude + ## `-Z force-unstable-if-unmarked` Using this flag looks like this: diff --git a/src/librustdoc/formats/cache.rs b/src/librustdoc/formats/cache.rs index 4989bd718c9f9..8768c1986ab20 100644 --- a/src/librustdoc/formats/cache.rs +++ b/src/librustdoc/formats/cache.rs @@ -3,6 +3,7 @@ use std::mem; use rustc_attr_data_structures::StabilityLevel; use rustc_data_structures::fx::{FxHashMap, FxHashSet, FxIndexMap, FxIndexSet}; use rustc_hir::def_id::{CrateNum, DefId, DefIdMap, DefIdSet}; +use rustc_metadata::creader::CStore; use rustc_middle::ty::{self, TyCtxt}; use rustc_span::Symbol; use tracing::debug; @@ -158,18 +159,31 @@ impl Cache { assert!(cx.external_traits.is_empty()); cx.cache.traits = mem::take(&mut krate.external_traits); + let render_options = &cx.render_options; + let extern_url_takes_precedence = render_options.extern_html_root_takes_precedence; + let dst = &render_options.output; + + // Make `--extern-html-root-url` support the same names as `--extern` whenever possible + let cstore = CStore::from_tcx(tcx); + for (name, extern_url) in &render_options.extern_html_root_urls { + if let Some(crate_num) = cstore.resolved_extern_crate(Symbol::intern(name)) { + let e = ExternalCrate { crate_num }; + let location = e.location(Some(extern_url), extern_url_takes_precedence, dst, tcx); + cx.cache.extern_locations.insert(e.crate_num, location); + } + } + // Cache where all our extern crates are located // FIXME: this part is specific to HTML so it'd be nice to remove it from the common code for &crate_num in tcx.crates(()) { let e = ExternalCrate { crate_num }; let name = e.name(tcx); - let render_options = &cx.render_options; - let extern_url = render_options.extern_html_root_urls.get(name.as_str()).map(|u| &**u); - let extern_url_takes_precedence = render_options.extern_html_root_takes_precedence; - let dst = &render_options.output; - let location = e.location(extern_url, extern_url_takes_precedence, dst, tcx); - cx.cache.extern_locations.insert(e.crate_num, location); + cx.cache.extern_locations.entry(e.crate_num).or_insert_with(|| { + let extern_url = + render_options.extern_html_root_urls.get(name.as_str()).map(|u| &**u); + e.location(extern_url, extern_url_takes_precedence, dst, tcx) + }); cx.cache.external_paths.insert(e.def_id(), (vec![name], ItemType::Module)); } diff --git a/tests/rustdoc/extern/extern-html-alias.rs b/tests/rustdoc/extern/extern-html-alias.rs new file mode 100644 index 0000000000000..a1a63d75b4ad9 --- /dev/null +++ b/tests/rustdoc/extern/extern-html-alias.rs @@ -0,0 +1,8 @@ +//@ compile-flags:-Z unstable-options --extern-html-root-url extern_prelude_name=https://renamed.example.com --extern-html-root-url empty=https://bad.invalid +//@ aux-crate:extern_prelude_name=empty.rs + +extern crate extern_prelude_name as internal_ident_name; + +//@ has extern_html_alias/index.html +//@ has - '//a/@href' 'https://renamed.example.com/empty/index.html' +pub use internal_ident_name as yet_different_name;