Skip to content

Commit 863216e

Browse files
committedFeb 13, 2022
rustdoc: Stop textually replacing Self in doc links before resolving them
Resolve it directly to a type / def-id instead. Also never pass `Self` to `Resolver`, it is useless because it's guaranteed that no resolution will be found.
·
1.89.01.61.0
1 parent 482b753 commit 863216e

File tree

1 file changed

+117
-92
lines changed

1 file changed

+117
-92
lines changed
 

‎src/librustdoc/passes/collect_intra_doc_links.rs

Lines changed: 117 additions & 92 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ use rustc_hir::def::{
1010
PerNS,
1111
};
1212
use rustc_hir::def_id::{CrateNum, DefId, CRATE_DEF_ID};
13-
use rustc_middle::ty::{DefIdTree, Ty, TyCtxt, TyKind};
13+
use rustc_middle::ty::{DefIdTree, Ty, TyCtxt};
1414
use rustc_middle::{bug, span_bug, ty};
1515
use rustc_session::lint::Lint;
1616
use rustc_span::hygiene::MacroKind;
@@ -26,7 +26,8 @@ use std::fmt::Write;
2626
use std::mem;
2727
use std::ops::Range;
2828

29-
use crate::clean::{self, utils::find_nearest_parent_module, Crate, Item, ItemLink, PrimitiveType};
29+
use crate::clean::{self, utils::find_nearest_parent_module};
30+
use crate::clean::{Crate, Item, ItemId, ItemLink, PrimitiveType};
3031
use crate::core::DocContext;
3132
use crate::html::markdown::{markdown_links, MarkdownLink};
3233
use crate::lint::{BROKEN_INTRA_DOC_LINKS, PRIVATE_INTRA_DOC_LINKS};
@@ -177,6 +178,8 @@ enum ResolutionFailure<'a> {
177178
/// The link failed to resolve. [`resolution_failure`] should look to see if there's
178179
/// a more helpful error that can be given.
179180
NotResolved {
181+
/// Item on which the link is resolved, used for resolving `Self`.
182+
item_id: ItemId,
180183
/// The scope the link was resolved in.
181184
module_id: DefId,
182185
/// If part of the link resolved, this has the `Res`.
@@ -343,6 +346,7 @@ impl ItemFragment {
343346

344347
#[derive(Clone, Debug, Hash, PartialEq, Eq)]
345348
struct ResolutionInfo {
349+
item_id: ItemId,
346350
module_id: DefId,
347351
dis: Option<Disambiguator>,
348352
path_str: String,
@@ -384,10 +388,12 @@ impl<'a, 'tcx> LinkCollector<'a, 'tcx> {
384388
fn variant_field<'path>(
385389
&self,
386390
path_str: &'path str,
391+
item_id: ItemId,
387392
module_id: DefId,
388393
) -> Result<(Res, Option<ItemFragment>), ErrorKind<'path>> {
389394
let tcx = self.cx.tcx;
390395
let no_res = || ResolutionFailure::NotResolved {
396+
item_id,
391397
module_id,
392398
partial_res: None,
393399
unresolved: path_str.into(),
@@ -410,13 +416,7 @@ impl<'a, 'tcx> LinkCollector<'a, 'tcx> {
410416
// If there's no third component, we saw `[a::b]` before and it failed to resolve.
411417
// So there's no partial res.
412418
.ok_or_else(no_res)?;
413-
let ty_res = self
414-
.cx
415-
.enter_resolver(|resolver| {
416-
resolver.resolve_str_path_error(DUMMY_SP, &path, TypeNS, module_id)
417-
})
418-
.and_then(|(_, res)| res.try_into())
419-
.map_err(|()| no_res())?;
419+
let ty_res = self.resolve_path(&path, TypeNS, item_id, module_id).ok_or_else(no_res)?;
420420

421421
match ty_res {
422422
Res::Def(DefKind::Enum, did) => {
@@ -437,6 +437,7 @@ impl<'a, 'tcx> LinkCollector<'a, 'tcx> {
437437
Ok((ty_res, Some(ItemFragment(FragmentKind::VariantField, field.did))))
438438
} else {
439439
Err(ResolutionFailure::NotResolved {
440+
item_id,
440441
module_id,
441442
partial_res: Some(Res::Def(DefKind::Enum, def.did)),
442443
unresolved: variant_field_name.to_string().into(),
@@ -448,6 +449,7 @@ impl<'a, 'tcx> LinkCollector<'a, 'tcx> {
448449
}
449450
}
450451
_ => Err(ResolutionFailure::NotResolved {
452+
item_id,
451453
module_id,
452454
partial_res: Some(ty_res),
453455
unresolved: variant_name.to_string().into(),
@@ -481,6 +483,7 @@ impl<'a, 'tcx> LinkCollector<'a, 'tcx> {
481483
fn resolve_macro(
482484
&self,
483485
path_str: &'a str,
486+
item_id: ItemId,
484487
module_id: DefId,
485488
) -> Result<Res, ResolutionFailure<'a>> {
486489
self.cx.enter_resolver(|resolver| {
@@ -499,19 +502,67 @@ impl<'a, 'tcx> LinkCollector<'a, 'tcx> {
499502
return Ok(res.try_into().unwrap());
500503
}
501504
Err(ResolutionFailure::NotResolved {
505+
item_id,
502506
module_id,
503507
partial_res: None,
504508
unresolved: path_str.into(),
505509
})
506510
})
507511
}
508512

513+
fn resolve_self_ty(&self, path_str: &str, ns: Namespace, item_id: ItemId) -> Option<Res> {
514+
if ns != TypeNS || path_str != "Self" {
515+
return None;
516+
}
517+
518+
let self_id = match item_id.as_def_id() {
519+
None => None,
520+
Some(did)
521+
if (matches!(self.cx.tcx.def_kind(did), DefKind::Field)
522+
&& matches!(
523+
self.cx.tcx.def_kind(self.cx.tcx.parent(did).unwrap()),
524+
DefKind::Variant
525+
)) =>
526+
{
527+
self.cx.tcx.parent(did).and_then(|item_id| self.cx.tcx.parent(item_id))
528+
}
529+
Some(did)
530+
if matches!(
531+
self.cx.tcx.def_kind(did),
532+
DefKind::AssocConst
533+
| DefKind::AssocFn
534+
| DefKind::AssocTy
535+
| DefKind::Variant
536+
| DefKind::Field
537+
) =>
538+
{
539+
self.cx.tcx.parent(did)
540+
}
541+
Some(did) => Some(did),
542+
};
543+
544+
self_id.and_then(|self_id| match self.cx.tcx.def_kind(self_id) {
545+
DefKind::Impl => self.def_id_to_res(self_id),
546+
def_kind => Some(Res::Def(def_kind, self_id)),
547+
})
548+
}
549+
509550
/// Convenience wrapper around `resolve_str_path_error`.
510551
///
511552
/// This also handles resolving `true` and `false` as booleans.
512553
/// NOTE: `resolve_str_path_error` knows only about paths, not about types.
513554
/// Associated items will never be resolved by this function.
514-
fn resolve_path(&self, path_str: &str, ns: Namespace, module_id: DefId) -> Option<Res> {
555+
fn resolve_path(
556+
&self,
557+
path_str: &str,
558+
ns: Namespace,
559+
item_id: ItemId,
560+
module_id: DefId,
561+
) -> Option<Res> {
562+
if let res @ Some(..) = self.resolve_self_ty(path_str, ns, item_id) {
563+
return res;
564+
}
565+
515566
let result = self.cx.enter_resolver(|resolver| {
516567
resolver
517568
.resolve_str_path_error(DUMMY_SP, path_str, ns, module_id)
@@ -532,10 +583,11 @@ impl<'a, 'tcx> LinkCollector<'a, 'tcx> {
532583
&mut self,
533584
path_str: &'path str,
534585
ns: Namespace,
586+
item_id: ItemId,
535587
module_id: DefId,
536588
user_fragment: &Option<String>,
537589
) -> Result<(Res, Option<UrlFragment>), ErrorKind<'path>> {
538-
let (res, rustdoc_fragment) = self.resolve_inner(path_str, ns, module_id)?;
590+
let (res, rustdoc_fragment) = self.resolve_inner(path_str, ns, item_id, module_id)?;
539591
let chosen_fragment = match (user_fragment, rustdoc_fragment) {
540592
(Some(_), Some(r_frag)) => {
541593
let diag_res = match r_frag {
@@ -555,9 +607,10 @@ impl<'a, 'tcx> LinkCollector<'a, 'tcx> {
555607
&mut self,
556608
path_str: &'path str,
557609
ns: Namespace,
610+
item_id: ItemId,
558611
module_id: DefId,
559612
) -> Result<(Res, Option<ItemFragment>), ErrorKind<'path>> {
560-
if let Some(res) = self.resolve_path(path_str, ns, module_id) {
613+
if let Some(res) = self.resolve_path(path_str, ns, item_id, module_id) {
561614
match res {
562615
// FIXME(#76467): make this fallthrough to lookup the associated
563616
// item a separate function.
@@ -585,6 +638,7 @@ impl<'a, 'tcx> LinkCollector<'a, 'tcx> {
585638
.ok_or_else(|| {
586639
debug!("found no `::`, assumming {} was correctly not in scope", item_name);
587640
ResolutionFailure::NotResolved {
641+
item_id,
588642
module_id,
589643
partial_res: None,
590644
unresolved: item_str.into(),
@@ -596,7 +650,7 @@ impl<'a, 'tcx> LinkCollector<'a, 'tcx> {
596650
// error instead and special case *only* modules with `#[doc(primitive)]`, not all
597651
// primitives.
598652
resolve_primitive(&path_root, TypeNS)
599-
.or_else(|| self.resolve_path(&path_root, TypeNS, module_id))
653+
.or_else(|| self.resolve_path(&path_root, TypeNS, item_id, module_id))
600654
.and_then(|ty_res| {
601655
let (res, fragment) =
602656
self.resolve_associated_item(ty_res, item_name, ns, module_id)?;
@@ -605,9 +659,10 @@ impl<'a, 'tcx> LinkCollector<'a, 'tcx> {
605659
})
606660
.unwrap_or_else(|| {
607661
if ns == Namespace::ValueNS {
608-
self.variant_field(path_str, module_id)
662+
self.variant_field(path_str, item_id, module_id)
609663
} else {
610664
Err(ResolutionFailure::NotResolved {
665+
item_id,
611666
module_id,
612667
partial_res: None,
613668
unresolved: path_root.into(),
@@ -730,7 +785,7 @@ impl<'a, 'tcx> LinkCollector<'a, 'tcx> {
730785
// Checks if item_name is a variant of the `SomeItem` enum
731786
if ns == TypeNS && def_kind == DefKind::Enum {
732787
match tcx.type_of(did).kind() {
733-
TyKind::Adt(adt_def, _) => {
788+
ty::Adt(adt_def, _) => {
734789
for variant in &adt_def.variants {
735790
if variant.name == item_name {
736791
return Some((
@@ -830,17 +885,18 @@ impl<'a, 'tcx> LinkCollector<'a, 'tcx> {
830885
&mut self,
831886
ns: Namespace,
832887
path_str: &str,
888+
item_id: ItemId,
833889
module_id: DefId,
834890
extra_fragment: &Option<String>,
835891
) -> Option<Res> {
836892
// resolve() can't be used for macro namespace
837893
let result = match ns {
838894
Namespace::MacroNS => self
839-
.resolve_macro(path_str, module_id)
895+
.resolve_macro(path_str, item_id, module_id)
840896
.map(|res| (res, None))
841897
.map_err(ErrorKind::from),
842898
Namespace::TypeNS | Namespace::ValueNS => {
843-
self.resolve(path_str, ns, module_id, extra_fragment)
899+
self.resolve(path_str, ns, item_id, module_id, extra_fragment)
844900
}
845901
};
846902

@@ -987,53 +1043,6 @@ impl<'a, 'tcx> DocVisitor for LinkCollector<'a, 'tcx> {
9871043
trace!("got parent node for {:?} {:?}, id {:?}", item.type_(), item.name, item.def_id);
9881044
}
9891045

990-
// find item's parent to resolve `Self` in item's docs below
991-
debug!("looking for the `Self` type");
992-
let self_id = match item.def_id.as_def_id() {
993-
None => None,
994-
Some(did)
995-
if (matches!(self.cx.tcx.def_kind(did), DefKind::Field)
996-
&& matches!(
997-
self.cx.tcx.def_kind(self.cx.tcx.parent(did).unwrap()),
998-
DefKind::Variant
999-
)) =>
1000-
{
1001-
self.cx.tcx.parent(did).and_then(|item_id| self.cx.tcx.parent(item_id))
1002-
}
1003-
Some(did)
1004-
if matches!(
1005-
self.cx.tcx.def_kind(did),
1006-
DefKind::AssocConst
1007-
| DefKind::AssocFn
1008-
| DefKind::AssocTy
1009-
| DefKind::Variant
1010-
| DefKind::Field
1011-
) =>
1012-
{
1013-
self.cx.tcx.parent(did)
1014-
}
1015-
Some(did) => Some(did),
1016-
};
1017-
1018-
// FIXME(jynelson): this shouldn't go through stringification, rustdoc should just use the DefId directly
1019-
let self_name = self_id.and_then(|self_id| {
1020-
if matches!(self.cx.tcx.def_kind(self_id), DefKind::Impl) {
1021-
// using `ty.to_string()` (or any variant) has issues with raw idents
1022-
let ty = self.cx.tcx.type_of(self_id);
1023-
let name = match ty.kind() {
1024-
ty::Adt(def, _) => Some(self.cx.tcx.item_name(def.did).to_string()),
1025-
other if other.is_primitive() => Some(ty.to_string()),
1026-
_ => None,
1027-
};
1028-
debug!("using type_of(): {:?}", name);
1029-
name
1030-
} else {
1031-
let name = self.cx.tcx.opt_item_name(self_id).map(|sym| sym.to_string());
1032-
debug!("using item_name(): {:?}", name);
1033-
name
1034-
}
1035-
});
1036-
10371046
let inner_docs = item.inner_docs(self.cx.tcx);
10381047

10391048
if item.is_mod() && inner_docs {
@@ -1055,7 +1064,7 @@ impl<'a, 'tcx> DocVisitor for LinkCollector<'a, 'tcx> {
10551064
// NOTE: if there are links that start in one crate and end in another, this will not resolve them.
10561065
// This is a degenerate case and it's not supported by rustdoc.
10571066
for md_link in markdown_links(&doc) {
1058-
let link = self.resolve_link(&item, &doc, &self_name, parent_node, krate, md_link);
1067+
let link = self.resolve_link(&item, &doc, parent_node, krate, md_link);
10591068
if let Some(link) = link {
10601069
self.cx.cache.intra_doc_links.entry(item.def_id).or_default().push(link);
10611070
}
@@ -1189,7 +1198,6 @@ impl LinkCollector<'_, '_> {
11891198
&mut self,
11901199
item: &Item,
11911200
dox: &str,
1192-
self_name: &Option<String>,
11931201
parent_node: Option<DefId>,
11941202
krate: CrateNum,
11951203
ori_link: MarkdownLink,
@@ -1259,19 +1267,8 @@ impl LinkCollector<'_, '_> {
12591267
};
12601268

12611269
let resolved_self;
1262-
// replace `Self` with suitable item's parent name
1263-
let is_lone_self = path_str == "Self";
12641270
let is_lone_crate = path_str == "crate";
1265-
if path_str.starts_with("Self::") || is_lone_self {
1266-
if let Some(ref name) = self_name {
1267-
if is_lone_self {
1268-
path_str = name;
1269-
} else {
1270-
resolved_self = format!("{}::{}", name, &path_str[6..]);
1271-
path_str = &resolved_self;
1272-
}
1273-
}
1274-
} else if path_str.starts_with("crate::") || is_lone_crate {
1271+
if path_str.starts_with("crate::") || is_lone_crate {
12751272
use rustc_span::def_id::CRATE_DEF_INDEX;
12761273

12771274
// HACK(jynelson): rustc_resolve thinks that `crate` is the crate currently being documented.
@@ -1291,6 +1288,7 @@ impl LinkCollector<'_, '_> {
12911288

12921289
let (mut res, fragment) = self.resolve_with_disambiguator_cached(
12931290
ResolutionInfo {
1291+
item_id: item.def_id,
12941292
module_id,
12951293
dis: disambiguator,
12961294
path_str: path_str.to_owned(),
@@ -1533,12 +1531,13 @@ impl LinkCollector<'_, '_> {
15331531
) -> Option<(Res, Option<UrlFragment>)> {
15341532
let disambiguator = key.dis;
15351533
let path_str = &key.path_str;
1534+
let item_id = key.item_id;
15361535
let base_node = key.module_id;
15371536
let extra_fragment = &key.extra_fragment;
15381537

15391538
match disambiguator.map(Disambiguator::ns) {
15401539
Some(expected_ns @ (ValueNS | TypeNS)) => {
1541-
match self.resolve(path_str, expected_ns, base_node, extra_fragment) {
1540+
match self.resolve(path_str, expected_ns, item_id, base_node, extra_fragment) {
15421541
Ok(res) => Some(res),
15431542
Err(ErrorKind::Resolve(box mut kind)) => {
15441543
// We only looked in one namespace. Try to give a better error if possible.
@@ -1547,9 +1546,13 @@ impl LinkCollector<'_, '_> {
15471546
// FIXME: really it should be `resolution_failure` that does this, not `resolve_with_disambiguator`
15481547
// See https://github.com/rust-lang/rust/pull/76955#discussion_r493953382 for a good approach
15491548
for new_ns in [other_ns, MacroNS] {
1550-
if let Some(res) =
1551-
self.check_full_res(new_ns, path_str, base_node, extra_fragment)
1552-
{
1549+
if let Some(res) = self.check_full_res(
1550+
new_ns,
1551+
path_str,
1552+
item_id,
1553+
base_node,
1554+
extra_fragment,
1555+
) {
15531556
kind = ResolutionFailure::WrongNamespace { res, expected_ns };
15541557
break;
15551558
}
@@ -1571,9 +1574,15 @@ impl LinkCollector<'_, '_> {
15711574
// Try everything!
15721575
let mut candidates = PerNS {
15731576
macro_ns: self
1574-
.resolve_macro(path_str, base_node)
1577+
.resolve_macro(path_str, item_id, base_node)
15751578
.map(|res| (res, extra_fragment.clone().map(UrlFragment::UserWritten))),
1576-
type_ns: match self.resolve(path_str, TypeNS, base_node, extra_fragment) {
1579+
type_ns: match self.resolve(
1580+
path_str,
1581+
TypeNS,
1582+
item_id,
1583+
base_node,
1584+
extra_fragment,
1585+
) {
15771586
Ok(res) => {
15781587
debug!("got res in TypeNS: {:?}", res);
15791588
Ok(res)
@@ -1584,7 +1593,13 @@ impl LinkCollector<'_, '_> {
15841593
}
15851594
Err(ErrorKind::Resolve(box kind)) => Err(kind),
15861595
},
1587-
value_ns: match self.resolve(path_str, ValueNS, base_node, extra_fragment) {
1596+
value_ns: match self.resolve(
1597+
path_str,
1598+
ValueNS,
1599+
item_id,
1600+
base_node,
1601+
extra_fragment,
1602+
) {
15881603
Ok(res) => Ok(res),
15891604
Err(ErrorKind::AnchorFailure(msg)) => {
15901605
anchor_failure(self.cx, diag, msg);
@@ -1643,14 +1658,18 @@ impl LinkCollector<'_, '_> {
16431658
}
16441659
}
16451660
Some(MacroNS) => {
1646-
match self.resolve_macro(path_str, base_node) {
1661+
match self.resolve_macro(path_str, item_id, base_node) {
16471662
Ok(res) => Some((res, extra_fragment.clone().map(UrlFragment::UserWritten))),
16481663
Err(mut kind) => {
16491664
// `resolve_macro` only looks in the macro namespace. Try to give a better error if possible.
16501665
for ns in [TypeNS, ValueNS] {
1651-
if let Some(res) =
1652-
self.check_full_res(ns, path_str, base_node, extra_fragment)
1653-
{
1666+
if let Some(res) = self.check_full_res(
1667+
ns,
1668+
path_str,
1669+
item_id,
1670+
base_node,
1671+
extra_fragment,
1672+
) {
16541673
kind =
16551674
ResolutionFailure::WrongNamespace { res, expected_ns: MacroNS };
16561675
break;
@@ -1977,11 +1996,16 @@ fn resolution_failure(
19771996
}
19781997
variants_seen.push(variant);
19791998

1980-
if let ResolutionFailure::NotResolved { module_id, partial_res, unresolved } =
1981-
&mut failure
1999+
if let ResolutionFailure::NotResolved {
2000+
item_id,
2001+
module_id,
2002+
partial_res,
2003+
unresolved,
2004+
} = &mut failure
19822005
{
19832006
use DefKind::*;
19842007

2008+
let item_id = *item_id;
19852009
let module_id = *module_id;
19862010
// FIXME(jynelson): this might conflict with my `Self` fix in #76467
19872011
// FIXME: maybe use itertools `collect_tuple` instead?
@@ -2005,7 +2029,8 @@ fn resolution_failure(
20052029
};
20062030
name = start;
20072031
for ns in [TypeNS, ValueNS, MacroNS] {
2008-
if let Some(res) = collector.check_full_res(ns, start, module_id, &None)
2032+
if let Some(res) =
2033+
collector.check_full_res(ns, start, item_id, module_id, &None)
20092034
{
20102035
debug!("found partial_res={:?}", res);
20112036
*partial_res = Some(res);

0 commit comments

Comments
 (0)
Please sign in to comment.