Rollup merge of #66704 - GuillaumeGomez:intra-doc-enum-variant-field, r=kinnison

Intra doc enum variant field

Part of #43466.

Add intra-doc link support for this:

```rust
enum Foo {
    X {
        y: u8, // can be found with Foo::X::y
    }
}
```

r? @kinnison
This commit is contained in:
Tyler Mandry 2019-11-27 15:28:39 -06:00 committed by GitHub
commit ddb616df90
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 90 additions and 23 deletions

View File

@ -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);

View File

@ -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'