diff --git a/src/librustdoc/passes/collect_intra_doc_links.rs b/src/librustdoc/passes/collect_intra_doc_links.rs index 783168d7bd6ec..d8f2dbca835d8 100644 --- a/src/librustdoc/passes/collect_intra_doc_links.rs +++ b/src/librustdoc/passes/collect_intra_doc_links.rs @@ -56,6 +56,64 @@ impl<'a, 'tcx> LinkCollector<'a, 'tcx> { } } + fn variant_field( + &self, + path_str: &str, + current_item: &Option, + module_id: syntax::ast::NodeId, + ) -> Result<(Res, Option), 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( @@ -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) @@ -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 { @@ -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( @@ -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), } } } @@ -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() { @@ -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); diff --git a/src/test/rustdoc/intra-doc-link-enum-struct-field.rs b/src/test/rustdoc/intra-doc-link-enum-struct-field.rs new file mode 100644 index 0000000000000..70bf343a9a5e5 --- /dev/null +++ b/src/test/rustdoc/intra-doc-link-enum-struct-field.rs @@ -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'