Skip to content

Commit ddb616d

Browse files
authored
Rollup merge of rust-lang#66704 - GuillaumeGomez:intra-doc-enum-variant-field, r=kinnison
Intra doc enum variant field Part of rust-lang#43466. Add intra-doc link support for this: ```rust enum Foo { X { y: u8, // can be found with Foo::X::y } } ``` r? @kinnison
2 parents 48b0fd2 + 97c427c commit ddb616d

File tree

2 files changed

+90
-23
lines changed

2 files changed

+90
-23
lines changed

src/librustdoc/passes/collect_intra_doc_links.rs

Lines changed: 76 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,64 @@ impl<'a, 'tcx> LinkCollector<'a, 'tcx> {
5656
}
5757
}
5858

59+
fn variant_field(
60+
&self,
61+
path_str: &str,
62+
current_item: &Option<String>,
63+
module_id: syntax::ast::NodeId,
64+
) -> Result<(Res, Option<String>), ErrorKind> {
65+
let cx = self.cx;
66+
67+
let mut split = path_str.rsplitn(3, "::");
68+
let variant_field_name = split
69+
.next()
70+
.map(|f| Symbol::intern(f))
71+
.ok_or(ErrorKind::ResolutionFailure)?;
72+
let variant_name = split
73+
.next()
74+
.map(|f| Symbol::intern(f))
75+
.ok_or(ErrorKind::ResolutionFailure)?;
76+
let path = split.next().map(|f| {
77+
if f == "self" || f == "Self" {
78+
if let Some(name) = current_item.as_ref() {
79+
return name.clone();
80+
}
81+
}
82+
f.to_owned()
83+
}).ok_or(ErrorKind::ResolutionFailure)?;
84+
let (_, ty_res) = cx.enter_resolver(|resolver| {
85+
resolver.resolve_str_path_error(DUMMY_SP, &path, TypeNS, module_id)
86+
}).map_err(|_| ErrorKind::ResolutionFailure)?;
87+
if let Res::Err = ty_res {
88+
return Err(ErrorKind::ResolutionFailure);
89+
}
90+
let ty_res = ty_res.map_id(|_| panic!("unexpected node_id"));
91+
match ty_res {
92+
Res::Def(DefKind::Enum, did) => {
93+
if cx.tcx.inherent_impls(did)
94+
.iter()
95+
.flat_map(|imp| cx.tcx.associated_items(*imp))
96+
.any(|item| item.ident.name == variant_name) {
97+
return Err(ErrorKind::ResolutionFailure);
98+
}
99+
match cx.tcx.type_of(did).kind {
100+
ty::Adt(def, _) if def.is_enum() => {
101+
if def.all_fields()
102+
.any(|item| item.ident.name == variant_field_name) {
103+
Ok((ty_res,
104+
Some(format!("variant.{}.field.{}",
105+
variant_name, variant_field_name))))
106+
} else {
107+
Err(ErrorKind::ResolutionFailure)
108+
}
109+
}
110+
_ => Err(ErrorKind::ResolutionFailure),
111+
}
112+
}
113+
_ => Err(ErrorKind::ResolutionFailure)
114+
}
115+
}
116+
59117
/// Resolves a string as a path within a particular namespace. Also returns an optional
60118
/// URL fragment in the case of variants and methods.
61119
fn resolve(
@@ -121,23 +179,18 @@ impl<'a, 'tcx> LinkCollector<'a, 'tcx> {
121179

122180
// Try looking for methods and associated items.
123181
let mut split = path_str.rsplitn(2, "::");
124-
let item_name = if let Some(first) = split.next() {
125-
Symbol::intern(first)
126-
} else {
127-
return Err(ErrorKind::ResolutionFailure)
128-
};
129-
130-
let mut path = if let Some(second) = split.next() {
131-
second.to_owned()
132-
} else {
133-
return Err(ErrorKind::ResolutionFailure)
134-
};
135-
136-
if path == "self" || path == "Self" {
137-
if let Some(name) = current_item.as_ref() {
138-
path = name.clone();
182+
let item_name = split.next()
183+
.map(|f| Symbol::intern(f))
184+
.ok_or(ErrorKind::ResolutionFailure)?;
185+
let path = split.next().map(|f| {
186+
if f == "self" || f == "Self" {
187+
if let Some(name) = current_item.as_ref() {
188+
return name.clone();
189+
}
139190
}
140-
}
191+
f.to_owned()
192+
}).ok_or(ErrorKind::ResolutionFailure)?;
193+
141194
if let Some(prim) = is_primitive(&path, TypeNS) {
142195
let did = primitive_impl(cx, &path).ok_or(ErrorKind::ResolutionFailure)?;
143196
return cx.tcx.associated_items(did)
@@ -154,7 +207,7 @@ impl<'a, 'tcx> LinkCollector<'a, 'tcx> {
154207
resolver.resolve_str_path_error(DUMMY_SP, &path, TypeNS, module_id)
155208
}).map_err(|_| ErrorKind::ResolutionFailure)?;
156209
if let Res::Err = ty_res {
157-
return Err(ErrorKind::ResolutionFailure);
210+
return self.variant_field(path_str, current_item, module_id);
158211
}
159212
let ty_res = ty_res.map_id(|_| panic!("unexpected node_id"));
160213
match ty_res {
@@ -170,7 +223,7 @@ impl<'a, 'tcx> LinkCollector<'a, 'tcx> {
170223
let out = match item.kind {
171224
ty::AssocKind::Method if ns == ValueNS => "method",
172225
ty::AssocKind::Const if ns == ValueNS => "associatedconstant",
173-
_ => return Err(ErrorKind::ResolutionFailure)
226+
_ => return self.variant_field(path_str, current_item, module_id),
174227
};
175228
if extra_fragment.is_some() {
176229
Err(ErrorKind::AnchorFailure(
@@ -211,10 +264,10 @@ impl<'a, 'tcx> LinkCollector<'a, 'tcx> {
211264
item.ident))))
212265
}
213266
} else {
214-
Err(ErrorKind::ResolutionFailure)
267+
self.variant_field(path_str, current_item, module_id)
215268
}
216269
}
217-
_ => Err(ErrorKind::ResolutionFailure),
270+
_ => self.variant_field(path_str, current_item, module_id),
218271
}
219272
}
220273
}
@@ -233,7 +286,7 @@ impl<'a, 'tcx> LinkCollector<'a, 'tcx> {
233286
"tymethod"
234287
}
235288
}
236-
_ => return Err(ErrorKind::ResolutionFailure)
289+
_ => return self.variant_field(path_str, current_item, module_id),
237290
};
238291

239292
if extra_fragment.is_some() {
@@ -249,10 +302,10 @@ impl<'a, 'tcx> LinkCollector<'a, 'tcx> {
249302
Ok((ty_res, Some(format!("{}.{}", kind, item_name))))
250303
}
251304
} else {
252-
Err(ErrorKind::ResolutionFailure)
305+
self.variant_field(path_str, current_item, module_id)
253306
}
254307
}
255-
_ => Err(ErrorKind::ResolutionFailure)
308+
_ => self.variant_field(path_str, current_item, module_id),
256309
}
257310
} else {
258311
debug!("attempting to resolve item without parent module: {}", path_str);
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
#![crate_name = "foo"]
2+
3+
pub enum Foo {
4+
X {
5+
y: u8,
6+
}
7+
}
8+
9+
/// Hello
10+
///
11+
/// I want [Foo::X::y].
12+
pub fn foo() {}
13+
14+
// @has foo/fn.foo.html '//a/@href' '../foo/enum.Foo.html#variant.X.field.y'

0 commit comments

Comments
 (0)