]> git.proxmox.com Git - rustc.git/blame - src/librustdoc/passes/collect_intra_doc_links/early.rs
New upstream version 1.68.2+dfsg1
[rustc.git] / src / librustdoc / passes / collect_intra_doc_links / early.rs
CommitLineData
04454e1e 1use crate::clean::Attributes;
5099ac24 2use crate::core::ResolverCaches;
04454e1e
FG
3use crate::passes::collect_intra_doc_links::preprocessed_markdown_links;
4use crate::passes::collect_intra_doc_links::{Disambiguator, PreprocessedMarkdownLink};
5099ac24
FG
5
6use rustc_ast::visit::{self, AssocCtxt, Visitor};
7use rustc_ast::{self as ast, ItemKind};
04454e1e
FG
8use rustc_data_structures::fx::FxHashMap;
9use rustc_hir::def::Namespace::*;
10use rustc_hir::def::{DefKind, Namespace, Res};
11use rustc_hir::def_id::{DefId, DefIdMap, DefIdSet, CRATE_DEF_ID};
5099ac24
FG
12use rustc_hir::TraitCandidate;
13use rustc_middle::ty::{DefIdTree, Visibility};
14use rustc_resolve::{ParentScope, Resolver};
04454e1e
FG
15use rustc_span::symbol::sym;
16use rustc_span::{Symbol, SyntaxContext};
94222f64 17
5099ac24 18use std::collections::hash_map::Entry;
94222f64 19use std::mem;
5099ac24 20
923072b8 21pub(crate) fn early_resolve_intra_doc_links(
5099ac24
FG
22 resolver: &mut Resolver<'_>,
23 krate: &ast::Crate,
04454e1e 24 document_private_items: bool,
5099ac24 25) -> ResolverCaches {
04454e1e
FG
26 let parent_scope =
27 ParentScope::module(resolver.expect_module(CRATE_DEF_ID.to_def_id()), resolver);
28 let mut link_resolver = EarlyDocLinkResolver {
5099ac24 29 resolver,
04454e1e 30 parent_scope,
5099ac24 31 visited_mods: Default::default(),
04454e1e
FG
32 markdown_links: Default::default(),
33 doc_link_resolutions: Default::default(),
5099ac24 34 traits_in_scope: Default::default(),
5099ac24 35 all_trait_impls: Default::default(),
04454e1e
FG
36 all_macro_rules: Default::default(),
37 document_private_items,
5099ac24
FG
38 };
39
5099ac24
FG
40 // Overridden `visit_item` below doesn't apply to the crate root,
41 // so we have to visit its attributes and reexports separately.
04454e1e
FG
42 link_resolver.resolve_doc_links_local(&krate.attrs);
43 link_resolver.process_module_children_or_reexports(CRATE_DEF_ID.to_def_id());
44 visit::walk_crate(&mut link_resolver, krate);
5099ac24
FG
45
46 // FIXME: somehow rustdoc is still missing crates even though we loaded all
47 // the known necessary crates. Load them all unconditionally until we find a way to fix this.
48 // DO NOT REMOVE THIS without first testing on the reproducer in
49 // https://github.com/jyn514/objr/commit/edcee7b8124abf0e4c63873e8422ff81beb11ebb
9c376795
FG
50 for (extern_name, _) in
51 link_resolver.resolver.sess().opts.externs.iter().filter(|(_, entry)| entry.add_prelude)
52 {
04454e1e 53 link_resolver.resolver.resolve_rustdoc_path(extern_name, TypeNS, parent_scope);
5099ac24
FG
54 }
55
2b03887a
FG
56 link_resolver.process_extern_impls();
57
5099ac24 58 ResolverCaches {
04454e1e
FG
59 markdown_links: Some(link_resolver.markdown_links),
60 doc_link_resolutions: link_resolver.doc_link_resolutions,
61 traits_in_scope: link_resolver.traits_in_scope,
04454e1e
FG
62 all_trait_impls: Some(link_resolver.all_trait_impls),
63 all_macro_rules: link_resolver.all_macro_rules,
5099ac24 64 }
94222f64
XL
65}
66
04454e1e
FG
67fn doc_attrs<'a>(attrs: impl Iterator<Item = &'a ast::Attribute>) -> Attributes {
68 Attributes::from_ast_iter(attrs.map(|attr| (attr, None)), true)
69}
70
71struct EarlyDocLinkResolver<'r, 'ra> {
5099ac24 72 resolver: &'r mut Resolver<'ra>,
04454e1e 73 parent_scope: ParentScope<'ra>,
5099ac24 74 visited_mods: DefIdSet,
04454e1e
FG
75 markdown_links: FxHashMap<String, Vec<PreprocessedMarkdownLink>>,
76 doc_link_resolutions: FxHashMap<(Symbol, Namespace, DefId), Option<Res<ast::NodeId>>>,
5099ac24 77 traits_in_scope: DefIdMap<Vec<TraitCandidate>>,
5099ac24 78 all_trait_impls: Vec<DefId>,
04454e1e
FG
79 all_macro_rules: FxHashMap<Symbol, Res<ast::NodeId>>,
80 document_private_items: bool,
94222f64
XL
81}
82
04454e1e 83impl<'ra> EarlyDocLinkResolver<'_, 'ra> {
5099ac24
FG
84 fn add_traits_in_scope(&mut self, def_id: DefId) {
85 // Calls to `traits_in_scope` are expensive, so try to avoid them if only possible.
86 // Keys in the `traits_in_scope` cache are always module IDs.
87 if let Entry::Vacant(entry) = self.traits_in_scope.entry(def_id) {
88 let module = self.resolver.get_nearest_non_block_module(def_id);
89 let module_id = module.def_id();
90 let entry = if module_id == def_id {
91 Some(entry)
92 } else if let Entry::Vacant(entry) = self.traits_in_scope.entry(module_id) {
93 Some(entry)
94 } else {
95 None
96 };
97 if let Some(entry) = entry {
98 entry.insert(self.resolver.traits_in_scope(
99 None,
100 &ParentScope::module(module, self.resolver),
101 SyntaxContext::root(),
102 None,
103 ));
104 }
105 }
106 }
107
5099ac24
FG
108 /// Add traits in scope for links in impls collected by the `collect-intra-doc-links` pass.
109 /// That pass filters impls using type-based information, but we don't yet have such
110 /// information here, so we just conservatively calculate traits in scope for *all* modules
111 /// having impls in them.
04454e1e
FG
112 fn process_extern_impls(&mut self) {
113 // Resolving links in already existing crates may trigger loading of new crates.
114 let mut start_cnum = 0;
115 loop {
116 let crates = Vec::from_iter(self.resolver.cstore().crates_untracked());
117 for &cnum in &crates[start_cnum..] {
04454e1e
FG
118 let all_trait_impls =
119 Vec::from_iter(self.resolver.cstore().trait_impls_in_crate_untracked(cnum));
120 let all_inherent_impls =
121 Vec::from_iter(self.resolver.cstore().inherent_impls_in_crate_untracked(cnum));
122 let all_incoherent_impls = Vec::from_iter(
123 self.resolver.cstore().incoherent_impls_in_crate_untracked(cnum),
124 );
125
2b03887a
FG
126 // Querying traits in scope is expensive so we try to prune the impl lists using
127 // privacy, private traits and impls from other crates are never documented in
04454e1e 128 // the current crate, and links in their doc comments are not resolved.
04454e1e
FG
129 for &(trait_def_id, impl_def_id, simplified_self_ty) in &all_trait_impls {
130 if self.resolver.cstore().visibility_untracked(trait_def_id).is_public()
131 && simplified_self_ty.and_then(|ty| ty.def()).map_or(true, |ty_def_id| {
132 self.resolver.cstore().visibility_untracked(ty_def_id).is_public()
133 })
134 {
2b03887a
FG
135 if self.visited_mods.insert(trait_def_id) {
136 self.resolve_doc_links_extern_impl(trait_def_id, false);
137 }
04454e1e
FG
138 self.resolve_doc_links_extern_impl(impl_def_id, false);
139 }
5099ac24 140 }
04454e1e
FG
141 for (ty_def_id, impl_def_id) in all_inherent_impls {
142 if self.resolver.cstore().visibility_untracked(ty_def_id).is_public() {
143 self.resolve_doc_links_extern_impl(impl_def_id, true);
144 }
145 }
146 for impl_def_id in all_incoherent_impls {
147 self.resolve_doc_links_extern_impl(impl_def_id, true);
5099ac24 148 }
04454e1e 149
04454e1e
FG
150 self.all_trait_impls
151 .extend(all_trait_impls.into_iter().map(|(_, def_id, _)| def_id));
5099ac24 152 }
04454e1e
FG
153
154 if crates.len() > start_cnum {
155 start_cnum = crates.len();
156 } else {
157 break;
5099ac24 158 }
04454e1e
FG
159 }
160 }
5099ac24 161
04454e1e 162 fn resolve_doc_links_extern_impl(&mut self, def_id: DefId, is_inherent: bool) {
923072b8 163 self.resolve_doc_links_extern_outer_fixme(def_id, def_id);
04454e1e 164 let assoc_item_def_ids = Vec::from_iter(
9c376795 165 self.resolver.cstore().associated_item_def_ids_untracked(def_id, self.resolver.sess()),
04454e1e
FG
166 );
167 for assoc_def_id in assoc_item_def_ids {
168 if !is_inherent || self.resolver.cstore().visibility_untracked(assoc_def_id).is_public()
169 {
923072b8 170 self.resolve_doc_links_extern_outer_fixme(assoc_def_id, def_id);
04454e1e 171 }
5099ac24
FG
172 }
173 }
174
923072b8
FG
175 // FIXME: replace all uses with `resolve_doc_links_extern_outer` to actually resolve links, not
176 // just add traits in scope. This may be expensive and require benchmarking and optimization.
177 fn resolve_doc_links_extern_outer_fixme(&mut self, def_id: DefId, scope_id: DefId) {
04454e1e
FG
178 if !self.resolver.cstore().may_have_doc_links_untracked(def_id) {
179 return;
180 }
04454e1e
FG
181 if let Some(parent_id) = self.resolver.opt_parent(scope_id) {
182 self.add_traits_in_scope(parent_id);
183 }
184 }
5099ac24 185
923072b8
FG
186 fn resolve_doc_links_extern_outer(&mut self, def_id: DefId, scope_id: DefId) {
187 if !self.resolver.cstore().may_have_doc_links_untracked(def_id) {
188 return;
189 }
9c376795
FG
190 let attrs = Vec::from_iter(
191 self.resolver.cstore().item_attrs_untracked(def_id, self.resolver.sess()),
192 );
923072b8
FG
193 let parent_scope = ParentScope::module(
194 self.resolver.get_nearest_non_block_module(
195 self.resolver.opt_parent(scope_id).unwrap_or(scope_id),
196 ),
197 self.resolver,
198 );
199 self.resolve_doc_links(doc_attrs(attrs.iter()), parent_scope);
200 }
201
04454e1e
FG
202 fn resolve_doc_links_extern_inner(&mut self, def_id: DefId) {
203 if !self.resolver.cstore().may_have_doc_links_untracked(def_id) {
204 return;
205 }
9c376795
FG
206 let attrs = Vec::from_iter(
207 self.resolver.cstore().item_attrs_untracked(def_id, self.resolver.sess()),
208 );
923072b8
FG
209 let parent_scope = ParentScope::module(self.resolver.expect_module(def_id), self.resolver);
210 self.resolve_doc_links(doc_attrs(attrs.iter()), parent_scope);
04454e1e
FG
211 }
212
213 fn resolve_doc_links_local(&mut self, attrs: &[ast::Attribute]) {
214 if !attrs.iter().any(|attr| attr.may_have_doc_links()) {
215 return;
216 }
217 self.resolve_doc_links(doc_attrs(attrs.iter()), self.parent_scope);
218 }
219
220 fn resolve_and_cache(
221 &mut self,
222 path_str: &str,
223 ns: Namespace,
224 parent_scope: &ParentScope<'ra>,
225 ) -> bool {
226 // FIXME: This caching may be incorrect in case of multiple `macro_rules`
227 // items with the same name in the same module.
228 self.doc_link_resolutions
229 .entry((Symbol::intern(path_str), ns, parent_scope.module.def_id()))
230 .or_insert_with_key(|(path, ns, _)| {
231 self.resolver.resolve_rustdoc_path(path.as_str(), *ns, *parent_scope)
232 })
233 .is_some()
234 }
235
236 fn resolve_doc_links(&mut self, attrs: Attributes, parent_scope: ParentScope<'ra>) {
237 let mut need_traits_in_scope = false;
238 for (doc_module, doc) in attrs.prepare_to_doc_link_resolution() {
239 assert_eq!(doc_module, None);
240 let mut tmp_links = mem::take(&mut self.markdown_links);
241 let links =
242 tmp_links.entry(doc).or_insert_with_key(|doc| preprocessed_markdown_links(doc));
243 for PreprocessedMarkdownLink(pp_link, _) in links {
244 if let Ok(pinfo) = pp_link {
245 // The logic here is a conservative approximation for path resolution in
246 // `resolve_with_disambiguator`.
247 if let Some(ns) = pinfo.disambiguator.map(Disambiguator::ns) {
248 if self.resolve_and_cache(&pinfo.path_str, ns, &parent_scope) {
249 continue;
250 }
251 }
252
253 // Resolve all namespaces due to no disambiguator or for diagnostics.
254 let mut any_resolved = false;
255 let mut need_assoc = false;
256 for ns in [TypeNS, ValueNS, MacroNS] {
257 if self.resolve_and_cache(&pinfo.path_str, ns, &parent_scope) {
258 any_resolved = true;
259 } else if ns != MacroNS {
260 need_assoc = true;
261 }
262 }
5099ac24 263
923072b8
FG
264 // Resolve all prefixes for type-relative resolution or for diagnostics.
265 if need_assoc || !any_resolved {
266 let mut path = &pinfo.path_str[..];
267 while let Some(idx) = path.rfind("::") {
268 path = &path[..idx];
269 need_traits_in_scope = true;
270 for ns in [TypeNS, ValueNS, MacroNS] {
271 self.resolve_and_cache(path, ns, &parent_scope);
272 }
273 }
04454e1e
FG
274 }
275 }
94222f64 276 }
04454e1e
FG
277 self.markdown_links = tmp_links;
278 }
279
280 if need_traits_in_scope {
281 self.add_traits_in_scope(parent_scope.module.def_id());
94222f64
XL
282 }
283 }
94222f64 284
5099ac24
FG
285 /// When reexports are inlined, they are replaced with item which they refer to, those items
286 /// may have links in their doc comments, those links are resolved at the item definition site,
287 /// so we need to know traits in scope at that definition site.
288 fn process_module_children_or_reexports(&mut self, module_id: DefId) {
289 if !self.visited_mods.insert(module_id) {
290 return; // avoid infinite recursion
291 }
292
293 for child in self.resolver.module_children_or_reexports(module_id) {
04454e1e
FG
294 // This condition should give a superset of `denied` from `fn clean_use_statement`.
295 if child.vis.is_public()
296 || self.document_private_items
297 && child.vis != Visibility::Restricted(module_id)
298 && module_id.is_local()
299 {
300 if let Some(def_id) = child.res.opt_def_id() && !def_id.is_local() {
301 let scope_id = match child.res {
2b03887a
FG
302 Res::Def(
303 DefKind::Variant
304 | DefKind::AssocTy
305 | DefKind::AssocFn
306 | DefKind::AssocConst,
307 ..,
308 ) => self.resolver.parent(def_id),
04454e1e
FG
309 _ => def_id,
310 };
311 self.resolve_doc_links_extern_outer(def_id, scope_id); // Outer attribute scope
312 if let Res::Def(DefKind::Mod, ..) = child.res {
313 self.resolve_doc_links_extern_inner(def_id); // Inner attribute scope
314 }
2b03887a 315 if let Res::Def(DefKind::Mod | DefKind::Enum | DefKind::Trait, ..) = child.res {
04454e1e
FG
316 self.process_module_children_or_reexports(def_id);
317 }
318 if let Res::Def(DefKind::Struct | DefKind::Union | DefKind::Variant, _) =
319 child.res
320 {
321 let field_def_ids = Vec::from_iter(
322 self.resolver
323 .cstore()
9c376795 324 .associated_item_def_ids_untracked(def_id, self.resolver.sess()),
04454e1e
FG
325 );
326 for field_def_id in field_def_ids {
327 self.resolve_doc_links_extern_outer(field_def_id, scope_id);
328 }
329 }
5099ac24
FG
330 }
331 }
332 }
c295e0f8 333 }
5099ac24 334}
c295e0f8 335
04454e1e 336impl Visitor<'_> for EarlyDocLinkResolver<'_, '_> {
94222f64 337 fn visit_item(&mut self, item: &ast::Item) {
04454e1e 338 self.resolve_doc_links_local(&item.attrs); // Outer attribute scope
5099ac24 339 if let ItemKind::Mod(..) = item.kind {
04454e1e
FG
340 let module_def_id = self.resolver.local_def_id(item.id).to_def_id();
341 let module = self.resolver.expect_module(module_def_id);
342 let old_module = mem::replace(&mut self.parent_scope.module, module);
343 let old_macro_rules = self.parent_scope.macro_rules;
344 self.resolve_doc_links_local(&item.attrs); // Inner attribute scope
345 self.process_module_children_or_reexports(module_def_id);
c295e0f8 346 visit::walk_item(self, item);
04454e1e
FG
347 if item
348 .attrs
349 .iter()
350 .all(|attr| !attr.has_name(sym::macro_use) && !attr.has_name(sym::macro_escape))
351 {
352 self.parent_scope.macro_rules = old_macro_rules;
353 }
354 self.parent_scope.module = old_module;
94222f64 355 } else {
04454e1e 356 match &item.kind {
2b03887a
FG
357 ItemKind::Impl(box ast::Impl { of_trait: Some(trait_ref), .. }) => {
358 if let Some(partial_res) = self.resolver.get_partial_res(trait_ref.ref_id)
359 && let Some(res) = partial_res.full_res()
360 && let Some(trait_def_id) = res.opt_def_id()
361 && !trait_def_id.is_local()
362 && self.visited_mods.insert(trait_def_id) {
363 self.resolve_doc_links_extern_impl(trait_def_id, false);
364 }
5099ac24
FG
365 self.all_trait_impls.push(self.resolver.local_def_id(item.id).to_def_id());
366 }
04454e1e
FG
367 ItemKind::MacroDef(macro_def) if macro_def.macro_rules => {
368 let (macro_rules_scope, res) =
369 self.resolver.macro_rules_scope(self.resolver.local_def_id(item.id));
370 self.parent_scope.macro_rules = macro_rules_scope;
371 self.all_macro_rules.insert(item.ident.name, res);
372 }
5099ac24
FG
373 _ => {}
374 }
c295e0f8 375 visit::walk_item(self, item);
94222f64
XL
376 }
377 }
c295e0f8 378
5099ac24 379 fn visit_assoc_item(&mut self, item: &ast::AssocItem, ctxt: AssocCtxt) {
04454e1e 380 self.resolve_doc_links_local(&item.attrs);
c295e0f8
XL
381 visit::walk_assoc_item(self, item, ctxt)
382 }
383
5099ac24 384 fn visit_foreign_item(&mut self, item: &ast::ForeignItem) {
04454e1e 385 self.resolve_doc_links_local(&item.attrs);
5099ac24 386 visit::walk_foreign_item(self, item)
c295e0f8
XL
387 }
388
389 fn visit_variant(&mut self, v: &ast::Variant) {
04454e1e 390 self.resolve_doc_links_local(&v.attrs);
c295e0f8
XL
391 visit::walk_variant(self, v)
392 }
5099ac24
FG
393
394 fn visit_field_def(&mut self, field: &ast::FieldDef) {
04454e1e 395 self.resolve_doc_links_local(&field.attrs);
5099ac24
FG
396 visit::walk_field_def(self, field)
397 }
398
04454e1e
FG
399 fn visit_block(&mut self, block: &ast::Block) {
400 let old_macro_rules = self.parent_scope.macro_rules;
401 visit::walk_block(self, block);
402 self.parent_scope.macro_rules = old_macro_rules;
403 }
404
5099ac24
FG
405 // NOTE: if doc-comments are ever allowed on other nodes (e.g. function parameters),
406 // then this will have to implement other visitor methods too.
94222f64 407}