]>
Commit | Line | Data |
---|---|---|
04454e1e | 1 | use crate::clean::Attributes; |
5099ac24 | 2 | use crate::core::ResolverCaches; |
04454e1e FG |
3 | use crate::passes::collect_intra_doc_links::preprocessed_markdown_links; |
4 | use crate::passes::collect_intra_doc_links::{Disambiguator, PreprocessedMarkdownLink}; | |
5099ac24 FG |
5 | |
6 | use rustc_ast::visit::{self, AssocCtxt, Visitor}; | |
7 | use rustc_ast::{self as ast, ItemKind}; | |
04454e1e FG |
8 | use rustc_data_structures::fx::FxHashMap; |
9 | use rustc_hir::def::Namespace::*; | |
10 | use rustc_hir::def::{DefKind, Namespace, Res}; | |
11 | use rustc_hir::def_id::{DefId, DefIdMap, DefIdSet, CRATE_DEF_ID}; | |
5099ac24 FG |
12 | use rustc_hir::TraitCandidate; |
13 | use rustc_middle::ty::{DefIdTree, Visibility}; | |
14 | use rustc_resolve::{ParentScope, Resolver}; | |
04454e1e FG |
15 | use rustc_span::symbol::sym; |
16 | use rustc_span::{Symbol, SyntaxContext}; | |
94222f64 | 17 | |
5099ac24 | 18 | use std::collections::hash_map::Entry; |
94222f64 | 19 | use std::mem; |
5099ac24 | 20 | |
923072b8 | 21 | pub(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 |
67 | fn doc_attrs<'a>(attrs: impl Iterator<Item = &'a ast::Attribute>) -> Attributes { |
68 | Attributes::from_ast_iter(attrs.map(|attr| (attr, None)), true) | |
69 | } | |
70 | ||
71 | struct 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 | 83 | impl<'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 | 336 | impl 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 | } |