Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
99 changes: 76 additions & 23 deletions src/librustdoc/passes/collect_intra_doc_links.rs
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,64 @@ impl<'a, 'tcx> LinkCollector<'a, 'tcx> {
}
}

fn variant_field(
&self,
path_str: &str,
current_item: &Option<String>,
module_id: syntax::ast::NodeId,
) -> Result<(Res, Option<String>), ErrorKind> {
let cx = self.cx;

let mut split = path_str.rsplitn(3, "::");
let variant_field_name = split
.next()
.map(|f| Symbol::intern(f))
.ok_or(ErrorKind::ResolutionFailure)?;
let variant_name = split
.next()
.map(|f| Symbol::intern(f))
.ok_or(ErrorKind::ResolutionFailure)?;
let path = split.next().map(|f| {
if f == "self" || f == "Self" {
if let Some(name) = current_item.as_ref() {
return name.clone();
}
}
f.to_owned()
}).ok_or(ErrorKind::ResolutionFailure)?;
let (_, ty_res) = cx.enter_resolver(|resolver| {
resolver.resolve_str_path_error(DUMMY_SP, &path, TypeNS, module_id)
}).map_err(|_| ErrorKind::ResolutionFailure)?;
if let Res::Err = ty_res {
return Err(ErrorKind::ResolutionFailure);
}
let ty_res = ty_res.map_id(|_| panic!("unexpected node_id"));
match ty_res {
Res::Def(DefKind::Enum, did) => {
if cx.tcx.inherent_impls(did)
.iter()
.flat_map(|imp| cx.tcx.associated_items(*imp))
.any(|item| item.ident.name == variant_name) {
return Err(ErrorKind::ResolutionFailure);
}
match cx.tcx.type_of(did).kind {
ty::Adt(def, _) if def.is_enum() => {
if def.all_fields()
.any(|item| item.ident.name == variant_field_name) {
Ok((ty_res,
Some(format!("variant.{}.field.{}",
variant_name, variant_field_name))))
} else {
Err(ErrorKind::ResolutionFailure)
}
}
_ => Err(ErrorKind::ResolutionFailure),
}
}
_ => Err(ErrorKind::ResolutionFailure)
}
}

/// Resolves a string as a path within a particular namespace. Also returns an optional
/// URL fragment in the case of variants and methods.
fn resolve(
Expand Down Expand Up @@ -121,23 +179,18 @@ impl<'a, 'tcx> LinkCollector<'a, 'tcx> {

// Try looking for methods and associated items.
let mut split = path_str.rsplitn(2, "::");
let item_name = if let Some(first) = split.next() {
Symbol::intern(first)
} else {
return Err(ErrorKind::ResolutionFailure)
};

let mut path = if let Some(second) = split.next() {
second.to_owned()
} else {
return Err(ErrorKind::ResolutionFailure)
};

if path == "self" || path == "Self" {
if let Some(name) = current_item.as_ref() {
path = name.clone();
let item_name = split.next()
.map(|f| Symbol::intern(f))
.ok_or(ErrorKind::ResolutionFailure)?;
let path = split.next().map(|f| {
if f == "self" || f == "Self" {
if let Some(name) = current_item.as_ref() {
return name.clone();
}
}
}
f.to_owned()
}).ok_or(ErrorKind::ResolutionFailure)?;

if let Some(prim) = is_primitive(&path, TypeNS) {
let did = primitive_impl(cx, &path).ok_or(ErrorKind::ResolutionFailure)?;
return cx.tcx.associated_items(did)
Expand All @@ -154,7 +207,7 @@ impl<'a, 'tcx> LinkCollector<'a, 'tcx> {
resolver.resolve_str_path_error(DUMMY_SP, &path, TypeNS, module_id)
}).map_err(|_| ErrorKind::ResolutionFailure)?;
if let Res::Err = ty_res {
return Err(ErrorKind::ResolutionFailure);
return self.variant_field(path_str, current_item, module_id);
}
let ty_res = ty_res.map_id(|_| panic!("unexpected node_id"));
match ty_res {
Expand All @@ -170,7 +223,7 @@ impl<'a, 'tcx> LinkCollector<'a, 'tcx> {
let out = match item.kind {
ty::AssocKind::Method if ns == ValueNS => "method",
ty::AssocKind::Const if ns == ValueNS => "associatedconstant",
_ => return Err(ErrorKind::ResolutionFailure)
_ => return self.variant_field(path_str, current_item, module_id),
};
if extra_fragment.is_some() {
Err(ErrorKind::AnchorFailure(
Expand Down Expand Up @@ -211,10 +264,10 @@ impl<'a, 'tcx> LinkCollector<'a, 'tcx> {
item.ident))))
}
} else {
Err(ErrorKind::ResolutionFailure)
self.variant_field(path_str, current_item, module_id)
}
}
_ => Err(ErrorKind::ResolutionFailure),
_ => self.variant_field(path_str, current_item, module_id),
}
}
}
Expand All @@ -233,7 +286,7 @@ impl<'a, 'tcx> LinkCollector<'a, 'tcx> {
"tymethod"
}
}
_ => return Err(ErrorKind::ResolutionFailure)
_ => return self.variant_field(path_str, current_item, module_id),
};

if extra_fragment.is_some() {
Expand All @@ -249,10 +302,10 @@ impl<'a, 'tcx> LinkCollector<'a, 'tcx> {
Ok((ty_res, Some(format!("{}.{}", kind, item_name))))
}
} else {
Err(ErrorKind::ResolutionFailure)
self.variant_field(path_str, current_item, module_id)
}
}
_ => Err(ErrorKind::ResolutionFailure)
_ => self.variant_field(path_str, current_item, module_id),
}
} else {
debug!("attempting to resolve item without parent module: {}", path_str);
Expand Down
14 changes: 14 additions & 0 deletions src/test/rustdoc/intra-doc-link-enum-struct-field.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
#![crate_name = "foo"]

pub enum Foo {
X {
y: u8,
}
}

/// Hello
///
/// I want [Foo::X::y].
pub fn foo() {}

// @has foo/fn.foo.html '//a/@href' '../foo/enum.Foo.html#variant.X.field.y'