mirror of
https://github.com/rust-lang/rust.git
synced 2026-04-27 18:57:42 +03:00
Fix relative path handling for --extern-html-root-url
This commit is contained in:
@@ -203,7 +203,14 @@ fn to_remote(url: impl ToString) -> ExternalLocation {
|
||||
if !url.ends_with('/') {
|
||||
url.push('/');
|
||||
}
|
||||
Remote(url)
|
||||
let is_absolute = url.starts_with('/')
|
||||
|| url.split_once("://").is_some_and(|(scheme, _)| {
|
||||
scheme.bytes().next().is_some_and(|b| b.is_ascii_alphabetic())
|
||||
&& scheme
|
||||
.bytes()
|
||||
.all(|b| b.is_ascii_alphanumeric() || matches!(b, b'+' | b'-' | b'.'))
|
||||
});
|
||||
Remote { url, is_absolute }
|
||||
}
|
||||
|
||||
// See if there's documentation generated into the local directory
|
||||
@@ -316,7 +323,7 @@ fn as_primitive(def_id: DefId, tcx: TyCtxt<'_>) -> Option<(DefId, PrimitiveType)
|
||||
#[derive(Debug)]
|
||||
pub(crate) enum ExternalLocation {
|
||||
/// Remote URL root of the external crate
|
||||
Remote(String),
|
||||
Remote { url: String, is_absolute: bool },
|
||||
/// This external crate can be found in the local doc/ folder
|
||||
Local,
|
||||
/// The external crate could not be found.
|
||||
|
||||
@@ -402,9 +402,10 @@ fn generate_macro_def_id_path(
|
||||
}
|
||||
|
||||
let url = match cache.extern_locations[&def_id.krate] {
|
||||
ExternalLocation::Remote(ref s) => {
|
||||
// `ExternalLocation::Remote` always end with a `/`.
|
||||
format!("{s}{path}", path = fmt::from_fn(|f| path.iter().joined("/", f)))
|
||||
ExternalLocation::Remote { ref url, is_absolute } => {
|
||||
let mut prefix = remote_url_prefix(url, is_absolute, cx.current.len());
|
||||
prefix.extend(path.iter().copied());
|
||||
prefix.finish()
|
||||
}
|
||||
ExternalLocation::Local => {
|
||||
// `root_path` always end with a `/`.
|
||||
@@ -458,10 +459,10 @@ fn generate_item_def_id_path(
|
||||
|
||||
let shortty = ItemType::from_def_id(def_id, tcx);
|
||||
let module_fqp = to_module_fqp(shortty, &fqp);
|
||||
let mut is_remote = false;
|
||||
let mut is_absolute = false;
|
||||
|
||||
let url_parts = url_parts(cx.cache(), def_id, module_fqp, &cx.current, &mut is_remote)?;
|
||||
let mut url_parts = make_href(root_path, shortty, url_parts, &fqp, is_remote);
|
||||
let url_parts = url_parts(cx.cache(), def_id, module_fqp, &cx.current, &mut is_absolute)?;
|
||||
let mut url_parts = make_href(root_path, shortty, url_parts, &fqp, is_absolute);
|
||||
if def_id != original_def_id {
|
||||
let kind = ItemType::from_def_id(original_def_id, tcx);
|
||||
url_parts = format!("{url_parts}#{kind}.{}", tcx.item_name(original_def_id))
|
||||
@@ -493,18 +494,29 @@ fn to_module_fqp(shortty: ItemType, fqp: &[Symbol]) -> &[Symbol] {
|
||||
if shortty == ItemType::Module { fqp } else { &fqp[..fqp.len() - 1] }
|
||||
}
|
||||
|
||||
fn remote_url_prefix(url: &str, is_absolute: bool, depth: usize) -> UrlPartsBuilder {
|
||||
let url = url.trim_end_matches('/');
|
||||
if is_absolute {
|
||||
UrlPartsBuilder::singleton(url)
|
||||
} else {
|
||||
let extra = depth.saturating_sub(1);
|
||||
let mut b: UrlPartsBuilder = iter::repeat_n("..", extra).collect();
|
||||
b.push(url);
|
||||
b
|
||||
}
|
||||
}
|
||||
|
||||
fn url_parts(
|
||||
cache: &Cache,
|
||||
def_id: DefId,
|
||||
module_fqp: &[Symbol],
|
||||
relative_to: &[Symbol],
|
||||
is_remote: &mut bool,
|
||||
is_absolute: &mut bool,
|
||||
) -> Result<UrlPartsBuilder, HrefError> {
|
||||
match cache.extern_locations[&def_id.krate] {
|
||||
ExternalLocation::Remote(ref s) => {
|
||||
*is_remote = true;
|
||||
let s = s.trim_end_matches('/');
|
||||
let mut builder = UrlPartsBuilder::singleton(s);
|
||||
ExternalLocation::Remote { ref url, is_absolute: abs } => {
|
||||
*is_absolute = abs;
|
||||
let mut builder = remote_url_prefix(url, abs, relative_to.len());
|
||||
builder.extend(module_fqp.iter().copied());
|
||||
Ok(builder)
|
||||
}
|
||||
@@ -518,9 +530,9 @@ fn make_href(
|
||||
shortty: ItemType,
|
||||
mut url_parts: UrlPartsBuilder,
|
||||
fqp: &[Symbol],
|
||||
is_remote: bool,
|
||||
is_absolute: bool,
|
||||
) -> String {
|
||||
if !is_remote && let Some(root_path) = root_path {
|
||||
if !is_absolute && let Some(root_path) = root_path {
|
||||
let root = root_path.trim_end_matches('/');
|
||||
url_parts.push_front(root);
|
||||
}
|
||||
@@ -583,7 +595,7 @@ pub(crate) fn href_with_root_path(
|
||||
}
|
||||
}
|
||||
|
||||
let mut is_remote = false;
|
||||
let mut is_absolute = false;
|
||||
let (fqp, shortty, url_parts) = match cache.paths.get(&did) {
|
||||
Some(&(ref fqp, shortty)) => (fqp, shortty, {
|
||||
let module_fqp = to_module_fqp(shortty, fqp.as_slice());
|
||||
@@ -597,7 +609,7 @@ pub(crate) fn href_with_root_path(
|
||||
let def_id_to_get = if root_path.is_some() { original_did } else { did };
|
||||
if let Some(&(ref fqp, shortty)) = cache.external_paths.get(&def_id_to_get) {
|
||||
let module_fqp = to_module_fqp(shortty, fqp);
|
||||
(fqp, shortty, url_parts(cache, did, module_fqp, relative_to, &mut is_remote)?)
|
||||
(fqp, shortty, url_parts(cache, did, module_fqp, relative_to, &mut is_absolute)?)
|
||||
} else if matches!(def_kind, DefKind::Macro(_)) {
|
||||
return generate_macro_def_id_path(did, cx, root_path);
|
||||
} else if did.is_local() {
|
||||
@@ -608,7 +620,7 @@ pub(crate) fn href_with_root_path(
|
||||
}
|
||||
};
|
||||
Ok(HrefInfo {
|
||||
url: make_href(root_path, shortty, url_parts, fqp, is_remote),
|
||||
url: make_href(root_path, shortty, url_parts, fqp, is_absolute),
|
||||
kind: shortty,
|
||||
rust_path: fqp.clone(),
|
||||
})
|
||||
@@ -762,12 +774,10 @@ fn primitive_link_fragment(
|
||||
}
|
||||
Some(&def_id) => {
|
||||
let loc = match m.extern_locations[&def_id.krate] {
|
||||
ExternalLocation::Remote(ref s) => {
|
||||
ExternalLocation::Remote { ref url, is_absolute } => {
|
||||
let cname_sym = ExternalCrate { crate_num: def_id.krate }.name(cx.tcx());
|
||||
let builder: UrlPartsBuilder =
|
||||
[s.as_str().trim_end_matches('/'), cname_sym.as_str()]
|
||||
.into_iter()
|
||||
.collect();
|
||||
let mut builder = remote_url_prefix(url, is_absolute, cx.current.len());
|
||||
builder.push(cname_sym.as_str());
|
||||
Some(builder)
|
||||
}
|
||||
ExternalLocation::Local => {
|
||||
|
||||
@@ -386,8 +386,9 @@ pub(crate) fn href_from_span(&self, span: clean::Span, with_lines: bool) -> Opti
|
||||
let e = ExternalCrate { crate_num: cnum };
|
||||
(e.name(self.tcx()), e.src_root(self.tcx()))
|
||||
}
|
||||
ExternalLocation::Remote(ref s) => {
|
||||
root = s.to_string();
|
||||
ExternalLocation::Remote { ref url, .. } => {
|
||||
// FIXME: relative extern URLs are not depth-adjusted for source pages
|
||||
root = url.to_string();
|
||||
let e = ExternalCrate { crate_num: cnum };
|
||||
(e.name(self.tcx()), e.src_root(self.tcx()))
|
||||
}
|
||||
|
||||
@@ -280,7 +280,8 @@ fn after_krate(self) -> Result<(), Error> {
|
||||
types::ExternalCrate {
|
||||
name: e.name(self.tcx).to_string(),
|
||||
html_root_url: match external_location {
|
||||
ExternalLocation::Remote(s) => Some(s.clone()),
|
||||
// FIXME: relative extern URLs are not resolved here
|
||||
ExternalLocation::Remote { url, .. } => Some(url.clone()),
|
||||
_ => None,
|
||||
},
|
||||
path: self
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
//@ compile-flags:-Z unstable-options --extern-html-root-url core=../ --extern-html-root-takes-precedence
|
||||
|
||||
// At depth 1 (top-level), the href should be ../core/...
|
||||
//@ has extern_html_root_url_relative/index.html
|
||||
//@ has - '//a/@href' '../core/iter/index.html'
|
||||
#[doc(no_inline)]
|
||||
pub use std::iter;
|
||||
|
||||
// At depth 2 (inside a module), the href should be ../../core/...
|
||||
pub mod nested {
|
||||
//@ has extern_html_root_url_relative/nested/index.html
|
||||
//@ has - '//a/@href' '../../core/iter/index.html'
|
||||
#[doc(no_inline)]
|
||||
pub use std::iter;
|
||||
}
|
||||
Reference in New Issue
Block a user