about summary refs log tree commit diff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/librustdoc/passes/collect_intra_doc_links.rs99
-rw-r--r--src/test/rustdoc/intra-doc-link-enum-struct-field.rs14
2 files changed, 90 insertions, 23 deletions
diff --git a/src/librustdoc/passes/collect_intra_doc_links.rs b/src/librustdoc/passes/collect_intra_doc_links.rs
index 783168d7bd6..d8f2dbca835 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<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(
@@ -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 00000000000..70bf343a9a5
--- /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'