Skip to content

Commit f93412f

Browse files
authored
Rollup merge of #83849 - jyn514:intra-doc-cleanup, r=bugadani
rustdoc: Cleanup handling of associated items for intra-doc links Helps with #83761 (right now the uses of the resolver are all intermingled with uses of the tyctxt). Best reviewed one commit at a time. r? ```@bugadani``` maybe? Feel free to reassign :)
2 parents 12d007d + 3b7e654 commit f93412f

File tree

6 files changed

+149
-150
lines changed

6 files changed

+149
-150
lines changed

compiler/rustc_resolve/src/lib.rs

+1
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
#![feature(nll)]
1919
#![cfg_attr(bootstrap, feature(or_patterns))]
2020
#![recursion_limit = "256"]
21+
#![allow(rustdoc::private_intra_doc_links)]
2122

2223
pub use rustc_hir::def::{Namespace, PerNS};
2324

src/librustdoc/passes/collect_intra_doc_links.rs

+102-135
Original file line numberDiff line numberDiff line change
@@ -368,55 +368,28 @@ impl<'a, 'tcx> LinkCollector<'a, 'tcx> {
368368
}
369369

370370
/// Given a primitive type, try to resolve an associated item.
371-
///
372-
/// HACK(jynelson): `item_str` is passed in instead of derived from `item_name` so the
373-
/// lifetimes on `&'path` will work.
374371
fn resolve_primitive_associated_item(
375372
&self,
376373
prim_ty: PrimitiveType,
377374
ns: Namespace,
378-
module_id: DefId,
379375
item_name: Symbol,
380-
item_str: &'path str,
381-
) -> Result<(Res, Option<String>), ErrorKind<'path>> {
376+
) -> Option<(Res, String, Option<(DefKind, DefId)>)> {
382377
let tcx = self.cx.tcx;
383378

384-
prim_ty
385-
.impls(tcx)
386-
.into_iter()
387-
.find_map(|&impl_| {
388-
tcx.associated_items(impl_)
389-
.find_by_name_and_namespace(tcx, Ident::with_dummy_span(item_name), ns, impl_)
390-
.map(|item| {
391-
let kind = item.kind;
392-
self.kind_side_channel.set(Some((kind.as_def_kind(), item.def_id)));
393-
match kind {
394-
ty::AssocKind::Fn => "method",
395-
ty::AssocKind::Const => "associatedconstant",
396-
ty::AssocKind::Type => "associatedtype",
397-
}
398-
})
399-
.map(|out| {
400-
(
401-
Res::Primitive(prim_ty),
402-
Some(format!("{}#{}.{}", prim_ty.as_str(), out, item_str)),
403-
)
404-
})
405-
})
406-
.ok_or_else(|| {
407-
debug!(
408-
"returning primitive error for {}::{} in {} namespace",
409-
prim_ty.as_str(),
410-
item_name,
411-
ns.descr()
412-
);
413-
ResolutionFailure::NotResolved {
414-
module_id,
415-
partial_res: Some(Res::Primitive(prim_ty)),
416-
unresolved: item_str.into(),
417-
}
418-
.into()
419-
})
379+
prim_ty.impls(tcx).into_iter().find_map(|&impl_| {
380+
tcx.associated_items(impl_)
381+
.find_by_name_and_namespace(tcx, Ident::with_dummy_span(item_name), ns, impl_)
382+
.map(|item| {
383+
let kind = item.kind;
384+
let out = match kind {
385+
ty::AssocKind::Fn => "method",
386+
ty::AssocKind::Const => "associatedconstant",
387+
ty::AssocKind::Type => "associatedtype",
388+
};
389+
let fragment = format!("{}#{}.{}", prim_ty.as_str(), out, item_name);
390+
(Res::Primitive(prim_ty), fragment, Some((kind.as_def_kind(), item.def_id)))
391+
})
392+
})
420393
}
421394

422395
/// Resolves a string as a macro.
@@ -490,8 +463,6 @@ impl<'a, 'tcx> LinkCollector<'a, 'tcx> {
490463
module_id: DefId,
491464
extra_fragment: &Option<String>,
492465
) -> Result<(Res, Option<String>), ErrorKind<'path>> {
493-
let tcx = self.cx.tcx;
494-
495466
if let Some(res) = self.resolve_path(path_str, ns, module_id) {
496467
match res {
497468
// FIXME(#76467): make this fallthrough to lookup the associated
@@ -534,29 +505,58 @@ impl<'a, 'tcx> LinkCollector<'a, 'tcx> {
534505
}
535506
})?;
536507

537-
// FIXME: are these both necessary?
538-
let ty_res = if let Some(ty_res) = resolve_primitive(&path_root, TypeNS)
508+
// FIXME(#83862): this arbitrarily gives precedence to primitives over modules to support
509+
// links to primitives when `#[doc(primitive)]` is present. It should give an ambiguity
510+
// error instead and special case *only* modules with `#[doc(primitive)]`, not all
511+
// primitives.
512+
resolve_primitive(&path_root, TypeNS)
539513
.or_else(|| self.resolve_path(&path_root, TypeNS, module_id))
540-
{
541-
ty_res
542-
} else {
543-
// FIXME: this is duplicated on the end of this function.
544-
return if ns == Namespace::ValueNS {
545-
self.variant_field(path_str, module_id)
546-
} else {
547-
Err(ResolutionFailure::NotResolved {
548-
module_id,
549-
partial_res: None,
550-
unresolved: path_root.into(),
514+
.and_then(|ty_res| {
515+
let (res, fragment, side_channel) =
516+
self.resolve_associated_item(ty_res, item_name, ns, module_id)?;
517+
let result = if extra_fragment.is_some() {
518+
let diag_res = side_channel.map_or(res, |(k, r)| Res::Def(k, r));
519+
Err(ErrorKind::AnchorFailure(AnchorFailure::RustdocAnchorConflict(diag_res)))
520+
} else {
521+
// HACK(jynelson): `clean` expects the type, not the associated item
522+
// but the disambiguator logic expects the associated item.
523+
// Store the kind in a side channel so that only the disambiguator logic looks at it.
524+
if let Some((kind, id)) = side_channel {
525+
self.kind_side_channel.set(Some((kind, id)));
526+
}
527+
Ok((res, Some(fragment)))
528+
};
529+
Some(result)
530+
})
531+
.unwrap_or_else(|| {
532+
if ns == Namespace::ValueNS {
533+
self.variant_field(path_str, module_id)
534+
} else {
535+
Err(ResolutionFailure::NotResolved {
536+
module_id,
537+
partial_res: None,
538+
unresolved: path_root.into(),
539+
}
540+
.into())
551541
}
552-
.into())
553-
};
554-
};
542+
})
543+
}
544+
545+
/// Returns:
546+
/// - None if no associated item was found
547+
/// - Some((_, _, Some(_))) if an item was found and should go through a side channel
548+
/// - Some((_, _, None)) otherwise
549+
fn resolve_associated_item(
550+
&mut self,
551+
root_res: Res,
552+
item_name: Symbol,
553+
ns: Namespace,
554+
module_id: DefId,
555+
) -> Option<(Res, String, Option<(DefKind, DefId)>)> {
556+
let tcx = self.cx.tcx;
555557

556-
let res = match ty_res {
557-
Res::Primitive(prim) => Some(
558-
self.resolve_primitive_associated_item(prim, ns, module_id, item_name, item_str),
559-
),
558+
match root_res {
559+
Res::Primitive(prim) => self.resolve_primitive_associated_item(prim, ns, item_name),
560560
Res::Def(
561561
DefKind::Struct
562562
| DefKind::Union
@@ -599,59 +599,42 @@ impl<'a, 'tcx> LinkCollector<'a, 'tcx> {
599599
ty::AssocKind::Const => "associatedconstant",
600600
ty::AssocKind::Type => "associatedtype",
601601
};
602-
Some(if extra_fragment.is_some() {
603-
Err(ErrorKind::AnchorFailure(AnchorFailure::RustdocAnchorConflict(ty_res)))
604-
} else {
605-
// HACK(jynelson): `clean` expects the type, not the associated item
606-
// but the disambiguator logic expects the associated item.
607-
// Store the kind in a side channel so that only the disambiguator logic looks at it.
608-
self.kind_side_channel.set(Some((kind.as_def_kind(), id)));
609-
Ok((ty_res, Some(format!("{}.{}", out, item_str))))
610-
})
611-
} else if ns == Namespace::ValueNS {
612-
debug!("looking for variants or fields named {} for {:?}", item_name, did);
613-
// FIXME(jynelson): why is this different from
614-
// `variant_field`?
615-
match tcx.type_of(did).kind() {
616-
ty::Adt(def, _) => {
617-
let field = if def.is_enum() {
618-
def.all_fields().find(|item| item.ident.name == item_name)
619-
} else {
620-
def.non_enum_variant()
621-
.fields
622-
.iter()
623-
.find(|item| item.ident.name == item_name)
624-
};
625-
field.map(|item| {
626-
if extra_fragment.is_some() {
627-
let res = Res::Def(
628-
if def.is_enum() {
629-
DefKind::Variant
630-
} else {
631-
DefKind::Field
632-
},
633-
item.did,
634-
);
635-
Err(ErrorKind::AnchorFailure(
636-
AnchorFailure::RustdocAnchorConflict(res),
637-
))
638-
} else {
639-
Ok((
640-
ty_res,
641-
Some(format!(
642-
"{}.{}",
643-
if def.is_enum() { "variant" } else { "structfield" },
644-
item.ident
645-
)),
646-
))
647-
}
648-
})
649-
}
650-
_ => None,
651-
}
652-
} else {
653-
None
602+
// HACK(jynelson): `clean` expects the type, not the associated item
603+
// but the disambiguator logic expects the associated item.
604+
// Store the kind in a side channel so that only the disambiguator logic looks at it.
605+
return Some((
606+
root_res,
607+
format!("{}.{}", out, item_name),
608+
Some((kind.as_def_kind(), id)),
609+
));
610+
}
611+
612+
if ns != Namespace::ValueNS {
613+
return None;
654614
}
615+
debug!("looking for variants or fields named {} for {:?}", item_name, did);
616+
// FIXME: this doesn't really belong in `associated_item` (maybe `variant_field` is better?)
617+
// NOTE: it's different from variant_field because it resolves fields and variants,
618+
// not variant fields (2 path segments, not 3).
619+
let def = match tcx.type_of(did).kind() {
620+
ty::Adt(def, _) => def,
621+
_ => return None,
622+
};
623+
let field = if def.is_enum() {
624+
def.all_fields().find(|item| item.ident.name == item_name)
625+
} else {
626+
def.non_enum_variant().fields.iter().find(|item| item.ident.name == item_name)
627+
}?;
628+
let kind = if def.is_enum() { DefKind::Variant } else { DefKind::Field };
629+
Some((
630+
root_res,
631+
format!(
632+
"{}.{}",
633+
if def.is_enum() { "variant" } else { "structfield" },
634+
field.ident
635+
),
636+
Some((kind, field.did)),
637+
))
655638
}
656639
Res::Def(DefKind::Trait, did) => tcx
657640
.associated_items(did)
@@ -669,27 +652,11 @@ impl<'a, 'tcx> LinkCollector<'a, 'tcx> {
669652
}
670653
};
671654

672-
if extra_fragment.is_some() {
673-
Err(ErrorKind::AnchorFailure(AnchorFailure::RustdocAnchorConflict(ty_res)))
674-
} else {
675-
let res = Res::Def(item.kind.as_def_kind(), item.def_id);
676-
Ok((res, Some(format!("{}.{}", kind, item_str))))
677-
}
655+
let res = Res::Def(item.kind.as_def_kind(), item.def_id);
656+
(res, format!("{}.{}", kind, item_name), None)
678657
}),
679658
_ => None,
680-
};
681-
res.unwrap_or_else(|| {
682-
if ns == Namespace::ValueNS {
683-
self.variant_field(path_str, module_id)
684-
} else {
685-
Err(ResolutionFailure::NotResolved {
686-
module_id,
687-
partial_res: Some(ty_res),
688-
unresolved: item_str.into(),
689-
}
690-
.into())
691-
}
692-
})
659+
}
693660
}
694661

695662
/// Used for reporting better errors.
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,27 @@
11
warning: public documentation for `DocMe` links to private item `DontDocMe`
2-
--> $DIR/private.rs:5:11
2+
--> $DIR/private.rs:7:11
33
|
4-
LL | /// docs [DontDocMe] [DontDocMe::f]
4+
LL | /// docs [DontDocMe] [DontDocMe::f] [DontDocMe::x]
55
| ^^^^^^^^^ this item is private
66
|
77
= note: `#[warn(rustdoc::private_intra_doc_links)]` on by default
88
= note: this link resolves only because you passed `--document-private-items`, but will break without
99

1010
warning: public documentation for `DocMe` links to private item `DontDocMe::f`
11-
--> $DIR/private.rs:5:23
11+
--> $DIR/private.rs:7:23
1212
|
13-
LL | /// docs [DontDocMe] [DontDocMe::f]
13+
LL | /// docs [DontDocMe] [DontDocMe::f] [DontDocMe::x]
1414
| ^^^^^^^^^^^^ this item is private
1515
|
1616
= note: this link resolves only because you passed `--document-private-items`, but will break without
1717

18-
warning: 2 warnings emitted
18+
warning: public documentation for `DocMe` links to private item `DontDocMe::x`
19+
--> $DIR/private.rs:7:38
20+
|
21+
LL | /// docs [DontDocMe] [DontDocMe::f] [DontDocMe::x]
22+
| ^^^^^^^^^^^^ this item is private
23+
|
24+
= note: this link resolves only because you passed `--document-private-items`, but will break without
25+
26+
warning: 3 warnings emitted
1927

Original file line numberDiff line numberDiff line change
@@ -1,19 +1,27 @@
11
warning: public documentation for `DocMe` links to private item `DontDocMe`
2-
--> $DIR/private.rs:5:11
2+
--> $DIR/private.rs:7:11
33
|
4-
LL | /// docs [DontDocMe] [DontDocMe::f]
4+
LL | /// docs [DontDocMe] [DontDocMe::f] [DontDocMe::x]
55
| ^^^^^^^^^ this item is private
66
|
77
= note: `#[warn(rustdoc::private_intra_doc_links)]` on by default
88
= note: this link will resolve properly if you pass `--document-private-items`
99

1010
warning: public documentation for `DocMe` links to private item `DontDocMe::f`
11-
--> $DIR/private.rs:5:23
11+
--> $DIR/private.rs:7:23
1212
|
13-
LL | /// docs [DontDocMe] [DontDocMe::f]
13+
LL | /// docs [DontDocMe] [DontDocMe::f] [DontDocMe::x]
1414
| ^^^^^^^^^^^^ this item is private
1515
|
1616
= note: this link will resolve properly if you pass `--document-private-items`
1717

18-
warning: 2 warnings emitted
18+
warning: public documentation for `DocMe` links to private item `DontDocMe::x`
19+
--> $DIR/private.rs:7:38
20+
|
21+
LL | /// docs [DontDocMe] [DontDocMe::f] [DontDocMe::x]
22+
| ^^^^^^^^^^^^ this item is private
23+
|
24+
= note: this link will resolve properly if you pass `--document-private-items`
25+
26+
warning: 3 warnings emitted
1927

src/test/rustdoc-ui/intra-doc/private.rs

+7-3
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,16 @@
22
// revisions: public private
33
// [private]compile-flags: --document-private-items
44

5-
/// docs [DontDocMe] [DontDocMe::f]
5+
// make sure to update `rustdoc/intra-doc/private.rs` if you update this file
6+
7+
/// docs [DontDocMe] [DontDocMe::f] [DontDocMe::x]
68
//~^ WARNING public documentation for `DocMe` links to private item `DontDocMe`
9+
//~| WARNING public documentation for `DocMe` links to private item `DontDocMe::x`
710
//~| WARNING public documentation for `DocMe` links to private item `DontDocMe::f`
8-
// FIXME: for [private] we should also make sure the link was actually generated
911
pub struct DocMe;
10-
struct DontDocMe;
12+
struct DontDocMe {
13+
x: usize,
14+
}
1115

1216
impl DontDocMe {
1317
fn f() {}

src/test/rustdoc/intra-doc/private.rs

+13-2
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,17 @@
11
#![crate_name = "private"]
22
// compile-flags: --document-private-items
3-
/// docs [DontDocMe]
3+
4+
// make sure to update `rustdoc-ui/intra-doc/private.rs` if you update this file
5+
6+
/// docs [DontDocMe] [DontDocMe::f] [DontDocMe::x]
47
// @has private/struct.DocMe.html '//*a[@href="../private/struct.DontDocMe.html"]' 'DontDocMe'
8+
// @has private/struct.DocMe.html '//*a[@href="../private/struct.DontDocMe.html#method.f"]' 'DontDocMe::f'
9+
// @has private/struct.DocMe.html '//*a[@href="../private/struct.DontDocMe.html#structfield.x"]' 'DontDocMe::x'
510
pub struct DocMe;
6-
struct DontDocMe;
11+
struct DontDocMe {
12+
x: usize,
13+
}
14+
15+
impl DontDocMe {
16+
fn f() {}
17+
}

0 commit comments

Comments
 (0)