Skip to content

Commit 09293be

Browse files
Handle anchors in intra doc links
1 parent f1b882b commit 09293be

File tree

1 file changed

+67
-11
lines changed

1 file changed

+67
-11
lines changed

src/librustdoc/passes/collect_intra_doc_links.rs

Lines changed: 67 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,8 @@ impl<'a, 'tcx> LinkCollector<'a, 'tcx> {
5757
path_str: &str,
5858
ns: Namespace,
5959
current_item: &Option<String>,
60-
parent_id: Option<hir::HirId>)
60+
parent_id: Option<hir::HirId>,
61+
extra_fragment: &Option<String>)
6162
-> Result<(Res, Option<String>), ()>
6263
{
6364
let cx = self.cx;
@@ -80,16 +81,23 @@ impl<'a, 'tcx> LinkCollector<'a, 'tcx> {
8081
let value = match res {
8182
Res::Def(DefKind::Method, _) | Res::Def(DefKind::AssocConst, _) => true,
8283
Res::Def(DefKind::AssocTy, _) => false,
83-
Res::Def(DefKind::Variant, _) => return handle_variant(cx, res),
84+
Res::Def(DefKind::Variant, _) => return handle_variant(cx, res, extra_fragment),
8485
// Not a trait item; just return what we found.
86+
Res::PrimTy(..) if extra_fragment.is_some() => {
87+
// TODO: warn in here! (and don't return Ok)
88+
return Ok((res, Some(path_str.to_owned())))
89+
}
8590
Res::PrimTy(..) => return Ok((res, Some(path_str.to_owned()))),
86-
_ => return Ok((res, None))
91+
_ => return Ok((res, extra_fragment.clone()))
8792
};
8893

8994
if value != (ns == ValueNS) {
9095
return Err(())
9196
}
9297
} else if let Some(prim) = is_primitive(path_str, ns) {
98+
//if extra_fragment.is_some() {
99+
// TODO: warn in here! (and don't return Ok)
100+
//}
93101
return Ok((prim, Some(path_str.to_owned())))
94102
} else {
95103
// If resolution failed, it may still be a method
@@ -153,6 +161,9 @@ impl<'a, 'tcx> LinkCollector<'a, 'tcx> {
153161
ty::AssocKind::Const if ns == ValueNS => "associatedconstant",
154162
_ => return Err(())
155163
};
164+
//if extra_fragment.is_some() {
165+
// TODO: warn in here! (and don't return Ok)
166+
//}
156167
Ok((ty_res, Some(format!("{}.{}", out, item_name))))
157168
} else {
158169
match cx.tcx.type_of(did).kind {
@@ -165,6 +176,9 @@ impl<'a, 'tcx> LinkCollector<'a, 'tcx> {
165176
.iter()
166177
.find(|item| item.ident.name == item_name)
167178
} {
179+
//if extra_fragment.is_some() {
180+
// TODO: warn in here! (and don't return Ok)
181+
//}
168182
Ok((ty_res,
169183
Some(format!("{}.{}",
170184
if def.is_enum() {
@@ -199,6 +213,9 @@ impl<'a, 'tcx> LinkCollector<'a, 'tcx> {
199213
_ => return Err(())
200214
};
201215

216+
//if extra_fragment.is_some() {
217+
// TODO: warn in here! (and don't return Ok)
218+
//}
202219
Ok((ty_res, Some(format!("{}.{}", kind, item_name))))
203220
} else {
204221
Err(())
@@ -289,6 +306,27 @@ impl<'a, 'tcx> DocFolder for LinkCollector<'a, 'tcx> {
289306
}
290307

291308
let link = ori_link.replace("`", "");
309+
let parts = link.split('#').collect::<Vec<_>>();
310+
let (link, extra_fragment) = if parts.len() > 2 {
311+
let mut diag = cx.tcx.struct_span_lint_hir(
312+
lint::builtin::INTRA_DOC_LINK_RESOLUTION_FAILURE,
313+
item_hir_id.unwrap(),
314+
span_of_attrs(&item.attrs).unwrap_or(item.source.span()),
315+
&format!("`[{}]` cannot be resolved, ignoring it...", ori_link),
316+
);
317+
// TODO: use the correct span!
318+
diag.span_label(DUMMY_SP, "only one `#` is allowed in a link");
319+
diag.emit();
320+
continue;
321+
} else if parts.len() == 2 {
322+
if parts[0].trim().is_empty() {
323+
// This is an anchor to an element of the current page, nothing to do in here!
324+
continue;
325+
}
326+
(parts[0].to_owned(), Some(parts[1].to_owned()))
327+
} else {
328+
(parts[0].to_owned(), None)
329+
};
292330
let (res, fragment) = {
293331
let mut kind = None;
294332
let path_str = if let Some(prefix) =
@@ -341,7 +379,8 @@ impl<'a, 'tcx> DocFolder for LinkCollector<'a, 'tcx> {
341379

342380
match kind {
343381
Some(ns @ ValueNS) => {
344-
if let Ok(res) = self.resolve(path_str, ns, &current_item, base_node) {
382+
if let Ok(res) = self.resolve(path_str, ns, &current_item, base_node,
383+
&extra_fragment) {
345384
res
346385
} else {
347386
resolution_failure(cx, &item, path_str, &dox, link_range);
@@ -352,7 +391,8 @@ impl<'a, 'tcx> DocFolder for LinkCollector<'a, 'tcx> {
352391
}
353392
}
354393
Some(ns @ TypeNS) => {
355-
if let Ok(res) = self.resolve(path_str, ns, &current_item, base_node) {
394+
if let Ok(res) = self.resolve(path_str, ns, &current_item, base_node,
395+
&extra_fragment) {
356396
res
357397
} else {
358398
resolution_failure(cx, &item, path_str, &dox, link_range);
@@ -363,18 +403,27 @@ impl<'a, 'tcx> DocFolder for LinkCollector<'a, 'tcx> {
363403
None => {
364404
// Try everything!
365405
let candidates = PerNS {
366-
macro_ns: macro_resolve(cx, path_str).map(|res| (res, None)),
406+
macro_ns: macro_resolve(cx, path_str)
407+
.map(|res| (res, extra_fragment.clone())),
367408
type_ns: self
368-
.resolve(path_str, TypeNS, &current_item, base_node)
409+
.resolve(path_str, TypeNS, &current_item, base_node, &extra_fragment)
369410
.ok(),
370411
value_ns: self
371-
.resolve(path_str, ValueNS, &current_item, base_node)
412+
.resolve(path_str, ValueNS, &current_item, base_node, &extra_fragment)
372413
.ok()
373414
.and_then(|(res, fragment)| {
374415
// Constructors are picked up in the type namespace.
375416
match res {
376417
Res::Def(DefKind::Ctor(..), _) | Res::SelfCtor(..) => None,
377-
_ => Some((res, fragment))
418+
_ => match (fragment, extra_fragment) {
419+
(Some(fragment), Some(_)) => {
420+
// Shouldn't happen but who knows?
421+
Some((res, Some(fragment)))
422+
}
423+
(fragment, None) | (None, fragment) => {
424+
Some((res, fragment))
425+
}
426+
},
378427
}
379428
}),
380429
};
@@ -402,7 +451,7 @@ impl<'a, 'tcx> DocFolder for LinkCollector<'a, 'tcx> {
402451
}
403452
Some(MacroNS) => {
404453
if let Some(res) = macro_resolve(cx, path_str) {
405-
(res, None)
454+
(res, extra_fragment)
406455
} else {
407456
resolution_failure(cx, &item, path_str, &dox, link_range);
408457
continue
@@ -637,7 +686,11 @@ fn ambiguity_error(
637686
}
638687

639688
/// Given an enum variant's res, return the res of its enum and the associated fragment.
640-
fn handle_variant(cx: &DocContext<'_>, res: Res) -> Result<(Res, Option<String>), ()> {
689+
fn handle_variant(
690+
cx: &DocContext<'_>,
691+
res: Res,
692+
extra_fragment: &Option<String>,
693+
) -> Result<(Res, Option<String>), ()> {
641694
use rustc::ty::DefIdTree;
642695

643696
let parent = if let Some(parent) = cx.tcx.parent(res.def_id()) {
@@ -647,6 +700,9 @@ fn handle_variant(cx: &DocContext<'_>, res: Res) -> Result<(Res, Option<String>)
647700
};
648701
let parent_def = Res::Def(DefKind::Enum, parent);
649702
let variant = cx.tcx.expect_variant_res(res);
703+
if extra_fragment.is_some() {
704+
// TODO warn in here! (and don't return ok)
705+
}
650706
Ok((parent_def, Some(format!("{}.v", variant.ident.name))))
651707
}
652708

0 commit comments

Comments
 (0)