1 use crate::clean::Attributes;
2 use crate::core::ResolverCaches;
3 use crate::passes::collect_intra_doc_links::preprocessed_markdown_links;
4 use crate::passes::collect_intra_doc_links::{Disambiguator, PreprocessedMarkdownLink};
6 use rustc_ast::visit::{self, AssocCtxt, Visitor};
7 use rustc_ast::{self as ast, ItemKind};
8 use rustc_ast_lowering::ResolverAstLowering;
9 use rustc_data_structures::fx::FxHashMap;
10 use rustc_hir::def::Namespace::*;
11 use rustc_hir::def::{DefKind, Namespace, Res};
12 use rustc_hir::def_id::{DefId, DefIdMap, DefIdSet, CRATE_DEF_ID};
13 use rustc_hir::TraitCandidate;
14 use rustc_middle::ty::{DefIdTree, Visibility};
15 use rustc_resolve::{ParentScope, Resolver};
16 use rustc_session::config::Externs;
17 use rustc_session::Session;
18 use rustc_span::symbol::sym;
19 use rustc_span::{Symbol, SyntaxContext};
21 use std::collections::hash_map::Entry;
24 crate fn early_resolve_intra_doc_links(
25 resolver: &mut Resolver<'_>,
29 document_private_items: bool,
32 ParentScope::module(resolver.expect_module(CRATE_DEF_ID.to_def_id()), resolver);
33 let mut link_resolver = EarlyDocLinkResolver {
37 visited_mods: Default::default(),
38 markdown_links: Default::default(),
39 doc_link_resolutions: Default::default(),
40 traits_in_scope: Default::default(),
41 all_traits: Default::default(),
42 all_trait_impls: Default::default(),
43 document_private_items,
46 // Overridden `visit_item` below doesn't apply to the crate root,
47 // so we have to visit its attributes and reexports separately.
48 link_resolver.resolve_doc_links_local(&krate.attrs);
49 link_resolver.process_module_children_or_reexports(CRATE_DEF_ID.to_def_id());
50 visit::walk_crate(&mut link_resolver, krate);
51 link_resolver.process_extern_impls();
53 // FIXME: somehow rustdoc is still missing crates even though we loaded all
54 // the known necessary crates. Load them all unconditionally until we find a way to fix this.
55 // DO NOT REMOVE THIS without first testing on the reproducer in
56 // https://github.com/jyn514/objr/commit/edcee7b8124abf0e4c63873e8422ff81beb11ebb
57 for (extern_name, _) in externs.iter().filter(|(_, entry)| entry.add_prelude) {
58 link_resolver.resolver.resolve_rustdoc_path(extern_name, TypeNS, parent_scope);
62 markdown_links: Some(link_resolver.markdown_links),
63 doc_link_resolutions: link_resolver.doc_link_resolutions,
64 traits_in_scope: link_resolver.traits_in_scope,
65 all_traits: Some(link_resolver.all_traits),
66 all_trait_impls: Some(link_resolver.all_trait_impls),
67 all_macro_rules: link_resolver.resolver.take_all_macro_rules(),
71 fn doc_attrs<'a>(attrs: impl Iterator<Item = &'a ast::Attribute>) -> Attributes {
72 Attributes::from_ast_iter(attrs.map(|attr| (attr, None)), true)
75 struct EarlyDocLinkResolver<'r, 'ra> {
76 resolver: &'r mut Resolver<'ra>,
78 parent_scope: ParentScope<'ra>,
79 visited_mods: DefIdSet,
80 markdown_links: FxHashMap<String, Vec<PreprocessedMarkdownLink>>,
81 doc_link_resolutions: FxHashMap<(Symbol, Namespace, DefId), Option<Res<ast::NodeId>>>,
82 traits_in_scope: DefIdMap<Vec<TraitCandidate>>,
83 all_traits: Vec<DefId>,
84 all_trait_impls: Vec<DefId>,
85 document_private_items: bool,
88 impl<'ra> EarlyDocLinkResolver<'_, 'ra> {
89 fn add_traits_in_scope(&mut self, def_id: DefId) {
90 // Calls to `traits_in_scope` are expensive, so try to avoid them if only possible.
91 // Keys in the `traits_in_scope` cache are always module IDs.
92 if let Entry::Vacant(entry) = self.traits_in_scope.entry(def_id) {
93 let module = self.resolver.get_nearest_non_block_module(def_id);
94 let module_id = module.def_id();
95 let entry = if module_id == def_id {
97 } else if let Entry::Vacant(entry) = self.traits_in_scope.entry(module_id) {
102 if let Some(entry) = entry {
103 entry.insert(self.resolver.traits_in_scope(
105 &ParentScope::module(module, self.resolver),
106 SyntaxContext::root(),
113 /// Add traits in scope for links in impls collected by the `collect-intra-doc-links` pass.
114 /// That pass filters impls using type-based information, but we don't yet have such
115 /// information here, so we just conservatively calculate traits in scope for *all* modules
116 /// having impls in them.
117 fn process_extern_impls(&mut self) {
118 // Resolving links in already existing crates may trigger loading of new crates.
119 let mut start_cnum = 0;
121 let crates = Vec::from_iter(self.resolver.cstore().crates_untracked());
122 for &cnum in &crates[start_cnum..] {
124 Vec::from_iter(self.resolver.cstore().traits_in_crate_untracked(cnum));
125 let all_trait_impls =
126 Vec::from_iter(self.resolver.cstore().trait_impls_in_crate_untracked(cnum));
127 let all_inherent_impls =
128 Vec::from_iter(self.resolver.cstore().inherent_impls_in_crate_untracked(cnum));
129 let all_incoherent_impls = Vec::from_iter(
130 self.resolver.cstore().incoherent_impls_in_crate_untracked(cnum),
133 // Querying traits in scope is expensive so we try to prune the impl and traits lists
134 // using privacy, private traits and impls from other crates are never documented in
135 // the current crate, and links in their doc comments are not resolved.
136 for &def_id in &all_traits {
137 if self.resolver.cstore().visibility_untracked(def_id) == Visibility::Public {
138 self.resolve_doc_links_extern_impl(def_id, false);
141 for &(trait_def_id, impl_def_id, simplified_self_ty) in &all_trait_impls {
142 if self.resolver.cstore().visibility_untracked(trait_def_id)
143 == Visibility::Public
144 && simplified_self_ty.and_then(|ty| ty.def()).map_or(true, |ty_def_id| {
145 self.resolver.cstore().visibility_untracked(ty_def_id)
146 == Visibility::Public
149 self.resolve_doc_links_extern_impl(impl_def_id, false);
152 for (ty_def_id, impl_def_id) in all_inherent_impls {
153 if self.resolver.cstore().visibility_untracked(ty_def_id) == Visibility::Public
155 self.resolve_doc_links_extern_impl(impl_def_id, true);
158 for impl_def_id in all_incoherent_impls {
159 self.resolve_doc_links_extern_impl(impl_def_id, true);
162 self.all_traits.extend(all_traits);
164 .extend(all_trait_impls.into_iter().map(|(_, def_id, _)| def_id));
167 if crates.len() > start_cnum {
168 start_cnum = crates.len();
175 fn resolve_doc_links_extern_impl(&mut self, def_id: DefId, is_inherent: bool) {
176 self.resolve_doc_links_extern_outer(def_id, def_id);
177 let assoc_item_def_ids = Vec::from_iter(
178 self.resolver.cstore().associated_item_def_ids_untracked(def_id, self.sess),
180 for assoc_def_id in assoc_item_def_ids {
182 || self.resolver.cstore().visibility_untracked(assoc_def_id) == Visibility::Public
184 self.resolve_doc_links_extern_outer(assoc_def_id, def_id);
189 fn resolve_doc_links_extern_outer(&mut self, def_id: DefId, scope_id: DefId) {
190 if !self.resolver.cstore().may_have_doc_links_untracked(def_id) {
193 // FIXME: actually resolve links, not just add traits in scope.
194 if let Some(parent_id) = self.resolver.opt_parent(scope_id) {
195 self.add_traits_in_scope(parent_id);
199 fn resolve_doc_links_extern_inner(&mut self, def_id: DefId) {
200 if !self.resolver.cstore().may_have_doc_links_untracked(def_id) {
203 // FIXME: actually resolve links, not just add traits in scope.
204 self.add_traits_in_scope(def_id);
207 fn resolve_doc_links_local(&mut self, attrs: &[ast::Attribute]) {
208 if !attrs.iter().any(|attr| attr.may_have_doc_links()) {
211 self.resolve_doc_links(doc_attrs(attrs.iter()), self.parent_scope);
214 fn resolve_and_cache(
218 parent_scope: &ParentScope<'ra>,
220 // FIXME: This caching may be incorrect in case of multiple `macro_rules`
221 // items with the same name in the same module.
222 self.doc_link_resolutions
223 .entry((Symbol::intern(path_str), ns, parent_scope.module.def_id()))
224 .or_insert_with_key(|(path, ns, _)| {
225 self.resolver.resolve_rustdoc_path(path.as_str(), *ns, *parent_scope)
230 fn resolve_doc_links(&mut self, attrs: Attributes, parent_scope: ParentScope<'ra>) {
231 let mut need_traits_in_scope = false;
232 for (doc_module, doc) in attrs.prepare_to_doc_link_resolution() {
233 assert_eq!(doc_module, None);
234 let mut tmp_links = mem::take(&mut self.markdown_links);
236 tmp_links.entry(doc).or_insert_with_key(|doc| preprocessed_markdown_links(doc));
237 for PreprocessedMarkdownLink(pp_link, _) in links {
238 if let Ok(pinfo) = pp_link {
239 // The logic here is a conservative approximation for path resolution in
240 // `resolve_with_disambiguator`.
241 if let Some(ns) = pinfo.disambiguator.map(Disambiguator::ns) {
242 if self.resolve_and_cache(&pinfo.path_str, ns, &parent_scope) {
247 // Resolve all namespaces due to no disambiguator or for diagnostics.
248 let mut any_resolved = false;
249 let mut need_assoc = false;
250 for ns in [TypeNS, ValueNS, MacroNS] {
251 if self.resolve_and_cache(&pinfo.path_str, ns, &parent_scope) {
253 } else if ns != MacroNS {
258 // FIXME: Resolve all prefixes for type-relative resolution or for diagnostics.
259 if (need_assoc || !any_resolved) && pinfo.path_str.contains("::") {
260 need_traits_in_scope = true;
264 self.markdown_links = tmp_links;
267 if need_traits_in_scope {
268 self.add_traits_in_scope(parent_scope.module.def_id());
272 /// When reexports are inlined, they are replaced with item which they refer to, those items
273 /// may have links in their doc comments, those links are resolved at the item definition site,
274 /// so we need to know traits in scope at that definition site.
275 fn process_module_children_or_reexports(&mut self, module_id: DefId) {
276 if !self.visited_mods.insert(module_id) {
277 return; // avoid infinite recursion
280 for child in self.resolver.module_children_or_reexports(module_id) {
281 // This condition should give a superset of `denied` from `fn clean_use_statement`.
282 if child.vis == Visibility::Public
283 || self.document_private_items
284 && child.vis != Visibility::Restricted(module_id)
285 && module_id.is_local()
287 if let Some(def_id) = child.res.opt_def_id() && !def_id.is_local() {
288 let scope_id = match child.res {
289 Res::Def(DefKind::Variant, ..) => self.resolver.parent(def_id),
292 self.resolve_doc_links_extern_outer(def_id, scope_id); // Outer attribute scope
293 if let Res::Def(DefKind::Mod, ..) = child.res {
294 self.resolve_doc_links_extern_inner(def_id); // Inner attribute scope
296 // `DefKind::Trait`s are processed in `process_extern_impls`.
297 if let Res::Def(DefKind::Mod | DefKind::Enum, ..) = child.res {
298 self.process_module_children_or_reexports(def_id);
300 if let Res::Def(DefKind::Struct | DefKind::Union | DefKind::Variant, _) =
303 let field_def_ids = Vec::from_iter(
306 .associated_item_def_ids_untracked(def_id, self.sess),
308 for field_def_id in field_def_ids {
309 self.resolve_doc_links_extern_outer(field_def_id, scope_id);
318 impl Visitor<'_> for EarlyDocLinkResolver<'_, '_> {
319 fn visit_item(&mut self, item: &ast::Item) {
320 self.resolve_doc_links_local(&item.attrs); // Outer attribute scope
321 if let ItemKind::Mod(..) = item.kind {
322 let module_def_id = self.resolver.local_def_id(item.id).to_def_id();
323 let module = self.resolver.expect_module(module_def_id);
324 let old_module = mem::replace(&mut self.parent_scope.module, module);
325 let old_macro_rules = self.parent_scope.macro_rules;
326 self.resolve_doc_links_local(&item.attrs); // Inner attribute scope
327 self.process_module_children_or_reexports(module_def_id);
328 visit::walk_item(self, item);
332 .all(|attr| !attr.has_name(sym::macro_use) && !attr.has_name(sym::macro_escape))
334 self.parent_scope.macro_rules = old_macro_rules;
336 self.parent_scope.module = old_module;
339 ItemKind::Trait(..) => {
340 self.all_traits.push(self.resolver.local_def_id(item.id).to_def_id());
342 ItemKind::Impl(box ast::Impl { of_trait: Some(..), .. }) => {
343 self.all_trait_impls.push(self.resolver.local_def_id(item.id).to_def_id());
345 ItemKind::MacroDef(macro_def) if macro_def.macro_rules => {
346 self.parent_scope.macro_rules =
347 self.resolver.macro_rules_scope(self.resolver.local_def_id(item.id));
351 visit::walk_item(self, item);
355 fn visit_assoc_item(&mut self, item: &ast::AssocItem, ctxt: AssocCtxt) {
356 self.resolve_doc_links_local(&item.attrs);
357 visit::walk_assoc_item(self, item, ctxt)
360 fn visit_foreign_item(&mut self, item: &ast::ForeignItem) {
361 self.resolve_doc_links_local(&item.attrs);
362 visit::walk_foreign_item(self, item)
365 fn visit_variant(&mut self, v: &ast::Variant) {
366 self.resolve_doc_links_local(&v.attrs);
367 visit::walk_variant(self, v)
370 fn visit_field_def(&mut self, field: &ast::FieldDef) {
371 self.resolve_doc_links_local(&field.attrs);
372 visit::walk_field_def(self, field)
375 fn visit_block(&mut self, block: &ast::Block) {
376 let old_macro_rules = self.parent_scope.macro_rules;
377 visit::walk_block(self, block);
378 self.parent_scope.macro_rules = old_macro_rules;
381 // NOTE: if doc-comments are ever allowed on other nodes (e.g. function parameters),
382 // then this will have to implement other visitor methods too.