Skip to content

Commit d70d79f

Browse files
committed
replace ad-hoc namespace enums
1 parent 537d4dd commit d70d79f

File tree

2 files changed

+72
-103
lines changed

2 files changed

+72
-103
lines changed

src/librustdoc/passes/collect_intra_doc_links.rs

+67-98
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
use errors::Applicability;
2-
use rustc::hir::def::Def;
2+
use rustc::hir::def::{Def, Namespace::{self, *}, PerNS};
33
use rustc::hir::def_id::DefId;
44
use rustc::hir;
55
use rustc::lint as lint;
@@ -36,46 +36,24 @@ pub fn collect_intra_doc_links(krate: Crate, cx: &DocContext<'_>) -> Crate {
3636
}
3737
}
3838

39-
#[derive(Debug)]
40-
enum PathKind {
41-
/// Either a value or type, but not a macro
42-
Unknown,
43-
/// Macro
44-
Macro,
45-
/// Values, functions, consts, statics (everything in the value namespace)
46-
Value,
47-
/// Types, traits (everything in the type namespace)
48-
Type,
49-
}
50-
5139
struct LinkCollector<'a, 'tcx> {
5240
cx: &'a DocContext<'tcx>,
5341
mod_ids: Vec<ast::NodeId>,
54-
is_nightly_build: bool,
55-
}
56-
57-
#[derive(Debug, Copy, Clone)]
58-
enum Namespace {
59-
Type,
60-
Value,
61-
Macro,
6242
}
6343

6444
impl<'a, 'tcx> LinkCollector<'a, 'tcx> {
6545
fn new(cx: &'a DocContext<'tcx>) -> Self {
6646
LinkCollector {
6747
cx,
6848
mod_ids: Vec::new(),
69-
is_nightly_build: UnstableFeatures::from_environment().is_nightly_build(),
7049
}
7150
}
7251

73-
/// Resolves a given string as a path, along with whether or not it is
74-
/// in the value namespace. Also returns an optional URL fragment in the case
75-
/// of variants and methods.
52+
/// Resolves a string as a path within a particular namespace. Also returns an optional
53+
/// URL fragment in the case of variants and methods.
7654
fn resolve(&self,
7755
path_str: &str,
78-
is_val: bool,
56+
ns: Namespace,
7957
current_item: &Option<String>,
8058
parent_id: Option<ast::NodeId>)
8159
-> Result<(Def, Option<String>), ()>
@@ -86,11 +64,11 @@ impl<'a, 'tcx> LinkCollector<'a, 'tcx> {
8664
// path.
8765
if let Some(id) = parent_id.or(self.mod_ids.last().cloned()) {
8866
// FIXME: `with_scope` requires the `NodeId` of a module.
89-
let result = cx.enter_resolver(|resolver| resolver.with_scope(id,
90-
|resolver| {
91-
resolver.resolve_str_path_error(DUMMY_SP,
92-
&path_str, is_val)
93-
}));
67+
let result = cx.enter_resolver(|resolver| {
68+
resolver.with_scope(id, |resolver| {
69+
resolver.resolve_str_path_error(DUMMY_SP, &path_str, ns == ValueNS)
70+
})
71+
});
9472

9573
if let Ok(result) = result {
9674
// In case this is a trait item, skip the
@@ -103,16 +81,16 @@ impl<'a, 'tcx> LinkCollector<'a, 'tcx> {
10381
_ => return Ok((result.def, None))
10482
};
10583

106-
if value != is_val {
84+
if value != (ns == ValueNS) {
10785
return Err(())
10886
}
109-
} else if let Some(prim) = is_primitive(path_str, is_val) {
87+
} else if let Some(prim) = is_primitive(path_str, ns) {
11088
return Ok((prim, Some(path_str.to_owned())))
11189
} else {
11290
// If resolution failed, it may still be a method
11391
// because methods are not handled by the resolver
11492
// If so, bail when we're not looking for a value.
115-
if !is_val {
93+
if ns != ValueNS {
11694
return Err(())
11795
}
11896
}
@@ -136,7 +114,7 @@ impl<'a, 'tcx> LinkCollector<'a, 'tcx> {
136114
path = name.clone();
137115
}
138116
}
139-
if let Some(prim) = is_primitive(&path, false) {
117+
if let Some(prim) = is_primitive(&path, TypeNS) {
140118
let did = primitive_impl(cx, &path).ok_or(())?;
141119
return cx.tcx.associated_items(did)
142120
.find(|item| item.ident.name == item_name)
@@ -160,8 +138,8 @@ impl<'a, 'tcx> LinkCollector<'a, 'tcx> {
160138
.find(|item| item.ident.name == item_name);
161139
if let Some(item) = item {
162140
let out = match item.kind {
163-
ty::AssociatedKind::Method if is_val => "method",
164-
ty::AssociatedKind::Const if is_val => "associatedconstant",
141+
ty::AssociatedKind::Method if ns == ValueNS => "method",
142+
ty::AssociatedKind::Const if ns == ValueNS => "associatedconstant",
165143
_ => return Err(())
166144
};
167145
Ok((ty.def, Some(format!("{}.{}", out, item_name))))
@@ -198,9 +176,9 @@ impl<'a, 'tcx> LinkCollector<'a, 'tcx> {
198176
.find(|item| item.ident.name == item_name);
199177
if let Some(item) = item {
200178
let kind = match item.kind {
201-
ty::AssociatedKind::Const if is_val => "associatedconstant",
202-
ty::AssociatedKind::Type if !is_val => "associatedtype",
203-
ty::AssociatedKind::Method if is_val => {
179+
ty::AssociatedKind::Const if ns == ValueNS => "associatedconstant",
180+
ty::AssociatedKind::Type if ns == TypeNS => "associatedtype",
181+
ty::AssociatedKind::Method if ns == ValueNS => {
204182
if item.defaultness.has_value() {
205183
"method"
206184
} else {
@@ -287,39 +265,35 @@ impl<'a, 'tcx> DocFolder for LinkCollector<'a, 'tcx> {
287265

288266
look_for_tests(&cx, &dox, &item, true);
289267

290-
if !self.is_nightly_build {
291-
return None;
292-
}
293-
294268
for (ori_link, link_range) in markdown_links(&dox) {
295269
// Bail early for real links.
296270
if ori_link.contains('/') {
297271
continue;
298272
}
299273
let link = ori_link.replace("`", "");
300274
let (def, fragment) = {
301-
let mut kind = PathKind::Unknown;
275+
let mut kind = None;
302276
let path_str = if let Some(prefix) =
303277
["struct@", "enum@", "type@",
304278
"trait@", "union@"].iter()
305279
.find(|p| link.starts_with(**p)) {
306-
kind = PathKind::Type;
280+
kind = Some(TypeNS);
307281
link.trim_start_matches(prefix)
308282
} else if let Some(prefix) =
309283
["const@", "static@",
310284
"value@", "function@", "mod@",
311285
"fn@", "module@", "method@"]
312286
.iter().find(|p| link.starts_with(**p)) {
313-
kind = PathKind::Value;
287+
kind = Some(ValueNS);
314288
link.trim_start_matches(prefix)
315289
} else if link.ends_with("()") {
316-
kind = PathKind::Value;
290+
kind = Some(ValueNS);
317291
link.trim_end_matches("()")
318292
} else if link.starts_with("macro@") {
319-
kind = PathKind::Macro;
293+
kind = Some(MacroNS);
320294
link.trim_start_matches("macro@")
321295
} else if link.ends_with('!') {
322-
kind = PathKind::Macro;
296+
kind = Some(MacroNS);
323297
link.trim_end_matches('!')
324298
} else {
325299
&link[..]
@@ -331,8 +305,8 @@ impl<'a, 'tcx> DocFolder for LinkCollector<'a, 'tcx> {
331305
}
332306

333307
match kind {
334-
PathKind::Value => {
335-
if let Ok(def) = self.resolve(path_str, true, &current_item, parent_node) {
308+
Some(ns @ ValueNS) => {
309+
if let Ok(def) = self.resolve(path_str, ns, &current_item, parent_node) {
336310
def
337311
} else {
338312
resolution_failure(cx, &item.attrs, path_str, &dox, link_range);
@@ -342,66 +316,58 @@ impl<'a, 'tcx> DocFolder for LinkCollector<'a, 'tcx> {
342316
continue;
343317
}
344318
}
345-
PathKind::Type => {
346-
if let Ok(def) = self.resolve(path_str, false, &current_item, parent_node) {
319+
Some(ns @ TypeNS) => {
320+
if let Ok(def) = self.resolve(path_str, ns, &current_item, parent_node) {
347321
def
348322
} else {
349323
resolution_failure(cx, &item.attrs, path_str, &dox, link_range);
350324
// This could just be a normal link.
351325
continue;
352326
}
353327
}
354-
PathKind::Unknown => {
328+
None => {
355329
// Try everything!
356-
let mut candidates = vec![];
357-
358-
if let Some(macro_def) = macro_resolve(cx, path_str) {
359-
candidates.push(((macro_def, None), Namespace::Macro));
360-
}
361-
362-
if let Ok(type_def) =
363-
self.resolve(path_str, false, &current_item, parent_node)
364-
{
365-
candidates.push((type_def, Namespace::Type));
366-
}
367-
368-
if let Ok(value_def) =
369-
self.resolve(path_str, true, &current_item, parent_node)
370-
{
371-
// Structs, variants, and mods exist in both namespaces, skip them.
372-
match value_def.0 {
373-
Def::StructCtor(..)
374-
| Def::Mod(..)
375-
| Def::Variant(..)
376-
| Def::VariantCtor(..)
377-
| Def::SelfCtor(..) => (),
378-
_ => candidates.push((value_def, Namespace::Value)),
379-
}
380-
}
330+
let candidates = PerNS {
331+
macro_ns: macro_resolve(cx, path_str).map(|def| (def, None)),
332+
type_ns: self
333+
.resolve(path_str, TypeNS, &current_item, parent_node)
334+
.ok(),
335+
value_ns: self
336+
.resolve(path_str, ValueNS, &current_item, parent_node)
337+
.ok()
338+
.and_then(|(def, fragment)| {
339+
// Constructors are picked up in the type namespace.
340+
match def {
341+
Def::StructCtor(..)
342+
| Def::VariantCtor(..)
343+
| Def::SelfCtor(..) => None,
344+
_ => Some((def, fragment))
345+
}
346+
}),
347+
};
381348

382-
if candidates.len() == 1 {
383-
candidates.remove(0).0
384-
} else if candidates.is_empty() {
349+
if candidates.is_empty() {
385350
resolution_failure(cx, &item.attrs, path_str, &dox, link_range);
386351
// this could just be a normal link
387352
continue;
388-
} else {
389-
let candidates = candidates.into_iter().map(|((def, _), ns)| {
390-
(def, ns)
391-
}).collect::<Vec<_>>();
353+
}
392354

355+
let is_unambiguous = candidates.clone().present_items().count() == 1;
356+
if is_unambiguous {
357+
candidates.present_items().next().unwrap()
358+
} else {
393359
ambiguity_error(
394360
cx,
395361
&item.attrs,
396362
path_str,
397363
&dox,
398364
link_range,
399-
&candidates,
365+
candidates.map(|candidate| candidate.map(|(def, _)| def)),
400366
);
401367
continue;
402368
}
403369
}
404-
PathKind::Macro => {
370+
Some(MacroNS) => {
405371
if let Some(def) = macro_resolve(cx, path_str) {
406372
(def, None)
407373
} else {
@@ -514,13 +480,16 @@ fn ambiguity_error(
514480
path_str: &str,
515481
dox: &str,
516482
link_range: Option<Range<usize>>,
517-
candidates: &[(Def, Namespace)],
483+
candidates: PerNS<Option<Def>>,
518484
) {
519485
let sp = span_of_attrs(attrs);
520486

521487
let mut msg = format!("`{}` is ", path_str);
522488

523-
match candidates {
489+
let candidates = [TypeNS, ValueNS, MacroNS].iter().filter_map(|&ns| {
490+
candidates[ns].map(|def| (def, ns))
491+
}).collect::<Vec<_>>();
492+
match candidates.as_slice() {
524493
[(first_def, _), (second_def, _)] => {
525494
msg += &format!(
526495
"both {} {} and {} {}",
@@ -568,9 +537,9 @@ fn ambiguity_error(
568537
(Def::Union(..), _) => "union",
569538
(Def::Trait(..), _) => "trait",
570539
(Def::Mod(..), _) => "module",
571-
(_, Namespace::Type) => "type",
572-
(_, Namespace::Value) => "value",
573-
(_, Namespace::Macro) => "macro",
540+
(_, TypeNS) => "type",
541+
(_, ValueNS) => "value",
542+
(_, MacroNS) => "macro",
574543
};
575544

576545
// FIXME: if this is an implied shortcut link, it's bad style to suggest `@`
@@ -646,11 +615,11 @@ const PRIMITIVES: &[(&str, Def)] = &[
646615
("char", Def::PrimTy(hir::PrimTy::Char)),
647616
];
648617

649-
fn is_primitive(path_str: &str, is_val: bool) -> Option<Def> {
650-
if is_val {
651-
None
652-
} else {
618+
fn is_primitive(path_str: &str, ns: Namespace) -> Option<Def> {
619+
if ns == TypeNS {
653620
PRIMITIVES.iter().find(|x| x.0 == path_str).map(|x| x.1)
621+
} else {
622+
None
654623
}
655624
}
656625

src/test/rustdoc-ui/intra-links-ambiguity.stderr

+5-5
Original file line numberDiff line numberDiff line change
@@ -32,15 +32,11 @@ help: to link to the function, add parentheses
3232
LL | /// [ambiguous()] is ambiguous. //~ERROR ambiguous
3333
| ^^^^^^^^^^^
3434

35-
error: `multi_conflict` is a macro, a struct, and a function
35+
error: `multi_conflict` is a struct, a function, and a macro
3636
--> $DIR/intra-links-ambiguity.rs:31:6
3737
|
3838
LL | /// [`multi_conflict`] is a three-way conflict. //~ERROR `multi_conflict`
3939
| ^^^^^^^^^^^^^^^^ ambiguous link
40-
help: to link to the macro, prefix with the item type
41-
|
42-
LL | /// [`macro@multi_conflict`] is a three-way conflict. //~ERROR `multi_conflict`
43-
| ^^^^^^^^^^^^^^^^^^^^^^
4440
help: to link to the struct, prefix with the item type
4541
|
4642
LL | /// [`struct@multi_conflict`] is a three-way conflict. //~ERROR `multi_conflict`
@@ -49,6 +45,10 @@ help: to link to the function, add parentheses
4945
|
5046
LL | /// [`multi_conflict()`] is a three-way conflict. //~ERROR `multi_conflict`
5147
| ^^^^^^^^^^^^^^^^^^
48+
help: to link to the macro, prefix with the item type
49+
|
50+
LL | /// [`macro@multi_conflict`] is a three-way conflict. //~ERROR `multi_conflict`
51+
| ^^^^^^^^^^^^^^^^^^^^^^
5252

5353
error: `type_and_value` is both a module and a constant
5454
--> $DIR/intra-links-ambiguity.rs:33:16

0 commit comments

Comments
 (0)