Repository navigation
Description
Rustdoc currently creates a copy of the resolver to use for intra-doc links:
Lines 350 to 354 in d474075
| crate fn create_resolver<'a>( | |
| externs: config::Externs, | |
| queries: &Queries<'a>, | |
| sess: &Session, | |
| ) -> Rc<RefCell<interface::BoxedResolver>> { |
This is a Terrible, Horrible, No Good, Very Bad Idea. In particular, it causes rustdoc's copy of the resolver and the TyCtxt to disagree about what crates exist:
- ICE when documenting code with intra-doc-links with types with the same name but different crates #66159 (comment)
- Fix intra-doc links for cross-crate re-exports of default trait methods #75176
- Fix impl being removed excessively #82496 (comment)
It's also distorting rustc_resolve, since not all of the outputs make sense to clone in the first place: #65625 (comment)
We should refactor rustdoc somehow to allow getting rid of Resolver::clone_outputs. @petrochenkov suggests moving anything that needs to touch the resolver before HIR lowering: #68427 (comment).
@petrochenkov what do you think about @eddyb's comment in #65625 (comment) ?
Why would cloning be needed? Is this that support for resolving things after rustc_resolve finishes?
In that case I'm not sure why we need to clone the outputs - is it to be able to keep the original resolver around?
Would it be possible for lower_to_hir to stop stealing the resolver?
rust/compiler/rustc_interface/src/queries.rs
Line 227 in d474075
| let resolver = peeked.1.steal(); |
Then rustdoc wouldn't need to clone it in the first place, it could just call queries.expansion().peek().1 whenever it needs access to the resolver.
See #68427 for previous discussion.
Implementation History
- Initial "early crate loader": rustdoc: Don't load all extern crates unconditionally #83738
- Don't require
clean::Items before resolving intra-doc links: rustdoc: Store intra-doc links in Cache instead of on items directly #83833 - Separate type-relative resolution from path resolution (at least somewhat): rustdoc: Cleanup handling of associated items for intra-doc links #83849
- Move early loader into
collect_intra_doc_links: rustdoc: Move crate loader to collect_intra_doc_links::early #84101 - Move all path resolution from late to early module and add new field on DocContext for partially resolved links
- Change
preprocess_linkto also take into account the hacks forSelf::andcrate:,// FIXME(jynelson): this shouldn't go through stringification, rustdoc should just use the DefId directly rust/src/librustdoc/passes/collect_intra_doc_links.rs
Lines 1124 to 1153 in ef52471
let resolved_self; // replace `Self` with suitable item's parent name let is_lone_self = path_str == "Self"; let is_lone_crate = path_str == "crate"; if path_str.starts_with("Self::") || is_lone_self { if let Some(ref name) = self_name { if is_lone_self { path_str = name; } else { resolved_self = format!("{}::{}", name, &path_str[6..]); path_str = &resolved_self; } } } else if path_str.starts_with("crate::") || is_lone_crate { use rustc_span::def_id::CRATE_DEF_INDEX; // HACK(jynelson): rustc_resolve thinks that `crate` is the crate currently being documented. // But rustdoc wants it to mean the crate this item was originally present in. // To work around this, remove it and resolve relative to the crate root instead. // HACK(jynelson)(2): If we just strip `crate::` then suddenly primitives become ambiguous // (consider `crate::char`). Instead, change it to `self::`. This works because 'self' is now the crate root. // FIXME(#78696): This doesn't always work. if is_lone_crate { path_str = "self"; } else { resolved_self = format!("self::{}", &path_str["crate::".len()..]); path_str = &resolved_self; } module_id = DefId { krate, index: CRATE_DEF_INDEX }; }
- Change
Activity
petrochenkov commentedon Apr 2, 2021
I'm not sure what do you mean by freezing the resolver outputs, but I meant the regular rustc's mode of operation
Rcto_resolver_outputsalways results in callingResolver::into_outputsrather thanResolver::clone_outputsResolver::into_outputsconsumes the resolver withResolverOutputsbeing the only thing left from it, i.e. it's a "frozen" summary of the resolver's work at a certain pointSo we need to achieve "freezing" the resolver outputs in rustdoc rather than to avoid it.
Regarding eddyb's comment.
Yes, rustdoc resolves things after creating tcx (which needs
ResolverOutputsto be created), so we have to keep the resolver.This needs to be avoided by moving all name resolution activity in rustdoc to an earlier point.
Not sure about the details of rustc_interface's work, but yes, resolver should be unique and should not be cloned.
[-]rustdoc: Don't freeze resolver outputs[/-][+]rustdoc: Stop cloning the resolver[/+]jyn514 commentedon Apr 2, 2021
Ok, I see, I updated the title.
I'm not quite sure I follow - is there a reason the tcx has to freeze the resolver into ResolverOutputs? Or could the tcx store the resolver itself? If it stored the resolver rustdoc could stop cloning it without major changes.
To be clear, I don't yet know how much work it would be for rustdoc to move all resolution before creating a TyCtxt, but the model where TyCtxt stores a resolver makes more sense in my head; or at least, it's more flexible.
Possible scheme for rustdoc to move resolution before TyCtxt
clean::Attributes::linksDocContext:intra_doc_links: FxHashMap<DefId, Vec<EarlyIntraDocLink>>IntraDocCrateLoader(from rustdoc: Don't load all extern crates unconditionally #83738) to fill out that field by handling more things that are currently incollect_intra_doc_links. I don't think it will be able to handle anything more thanresolve_path:rust/src/librustdoc/passes/collect_intra_doc_links.rs
Line 469 in d1065e6
collect_intra_doc_linksto remove all uses of the resolver.For context, these are the current uses of the resolver in collect_intra_doc_links:
rust/src/librustdoc/passes/collect_intra_doc_links.rs
Lines 762 to 772 in d1065e6
rust/src/librustdoc/passes/collect_intra_doc_links.rs
Lines 425 to 431 in d1065e6
rust/src/librustdoc/passes/collect_intra_doc_links.rs
Lines 319 to 323 in d1065e6
rust/src/librustdoc/passes/collect_intra_doc_links.rs
Lines 469 to 472 in d1065e6
petrochenkov commentedon Apr 2, 2021
It may be possible.
Resolver contains a lot of early compilation stuff like
NodeIds that is out of place in tcx though.Also it may be preferable to finish all the resolution activities as early as possible (at least those that may result in loading new crates).
I think we may end up with some scheme working similarly to this if incremental compilation and queries advance so much that we'll start skipping resolution of paths if they are not actually used, but that would probably be a very different resolver.
For now, I'd prefer to normalize to what rustc does and have a single scheme with resolver working early instead.
Paths in doc comments are not fundamentally different from paths in e.g. expressions, and if we visit and resolve paths early in general, then paths in doc comments can be visited and resolved early as well.
(If we migrate to something like "resolver in tcx" in the future, then rustc and rustdoc will also migrate to it together.)
jyn514 commentedon Apr 4, 2021
Done in #83833 (although I need to clean it up before it can be merged).
jyn514 commentedon Apr 4, 2021
This isn't currently possible because resolving
Selfgoes through the TyCtxt to get the name instead of the resolver:rust/src/librustdoc/passes/collect_intra_doc_links.rs
Lines 868 to 885 in 2616ab1
Here is a fix (it uses a tcx currently, but the same API is available on CStore): 5d5be02
Original ramblings before I realized this was easy
There are two possible ways forward:
.def_kindwith only a Resolver - looks like I can do that withCStore::def_kind, and I can get a CStore fromResolver::cstore.Selfwith the resolver directly (just an equivalent ofopt_item_nameshould be enough). (This is 5d5be02)jyn514 commentedon Apr 4, 2021
Oh wait no I'm dumb, 5d5be02 only helps if the item isn't an impl block. @petrochenkov Is there a way to resolve the
Selftype of an impl block? I triedresolver.resolve_str_path_error(DUMMY_SP, "Self", TypeNS, self_id), but that panics because it's not a module.If not it's not a giant deal, I'll just have to figure out how to fix the FIXME on line 868.
jyn514 commentedon Apr 4, 2021
Ugh, I have to figure out how to replicate this bit somehow:
rust/src/librustdoc/passes/collect_intra_doc_links.rs
Lines 1824 to 1833 in 2616ab1
Maybe the early pass could do that? I think the late pass for type-relative paths will need to have a partial resolution anyway, so it shouldn't be too much more work.
petrochenkov commentedon Apr 4, 2021
rustckeeps the "current impl" (or other item providingSelf) in the late resolution visitor walking AST (seefn with_self_rib_ns).The analogous visitor in rustdoc is
IntraLinkCrateLoaderright now.I'm not sure how rustdoc resolves
Selfnow, but it cannot do it throughResolverbecause resolver doesn't provide any methods able to resolve it. So it's probably fine for rustdoc to just continue doing what it's doing now.58 remaining items
Auto merge of #93805 - petrochenkov:doclinkself, r=camelid,GuillaumeG…