-
-
Notifications
You must be signed in to change notification settings - Fork 15.3k
Expand file tree
/
Copy pathearly.rs
More file actions
234 lines (208 loc) · 9.67 KB
/
Copy pathearly.rs
File metadata and controls
234 lines (208 loc) · 9.67 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
use crate::clean;
use crate::core::ResolverCaches;
use crate::html::markdown::markdown_links;
use crate::passes::collect_intra_doc_links::preprocess_link;
use rustc_ast::visit::{self, AssocCtxt, Visitor};
use rustc_ast::{self as ast, ItemKind};
use rustc_ast_lowering::ResolverAstLowering;
use rustc_hir::def::Namespace::TypeNS;
use rustc_hir::def::{DefKind, Res};
use rustc_hir::def_id::{DefId, DefIdMap, DefIdSet, LocalDefId, CRATE_DEF_ID};
use rustc_hir::TraitCandidate;
use rustc_middle::ty::{DefIdTree, Visibility};
use rustc_resolve::{ParentScope, Resolver};
use rustc_session::config::Externs;
use rustc_span::SyntaxContext;
use std::collections::hash_map::Entry;
use std::mem;
crate fn early_resolve_intra_doc_links(
resolver: &mut Resolver<'_>,
krate: &ast::Crate,
externs: Externs,
) -> ResolverCaches {
let mut loader = IntraLinkCrateLoader {
resolver,
current_mod: CRATE_DEF_ID,
visited_mods: Default::default(),
traits_in_scope: Default::default(),
all_traits: Default::default(),
all_trait_impls: Default::default(),
};
// Overridden `visit_item` below doesn't apply to the crate root,
// so we have to visit its attributes and reexports separately.
loader.load_links_in_attrs(&krate.attrs);
loader.process_module_children_or_reexports(CRATE_DEF_ID.to_def_id());
visit::walk_crate(&mut loader, krate);
loader.add_foreign_traits_in_scope();
// FIXME: somehow rustdoc is still missing crates even though we loaded all
// the known necessary crates. Load them all unconditionally until we find a way to fix this.
// DO NOT REMOVE THIS without first testing on the reproducer in
// https://github.com/jyn514/objr/commit/edcee7b8124abf0e4c63873e8422ff81beb11ebb
for (extern_name, _) in externs.iter().filter(|(_, entry)| entry.add_prelude) {
loader.resolver.resolve_rustdoc_path(extern_name, TypeNS, CRATE_DEF_ID.to_def_id());
}
ResolverCaches {
traits_in_scope: loader.traits_in_scope,
all_traits: Some(loader.all_traits),
all_trait_impls: Some(loader.all_trait_impls),
}
}
struct IntraLinkCrateLoader<'r, 'ra> {
resolver: &'r mut Resolver<'ra>,
current_mod: LocalDefId,
visited_mods: DefIdSet,
traits_in_scope: DefIdMap<Vec<TraitCandidate>>,
all_traits: Vec<DefId>,
all_trait_impls: Vec<DefId>,
}
impl IntraLinkCrateLoader<'_, '_> {
fn add_traits_in_scope(&mut self, def_id: DefId) {
// Calls to `traits_in_scope` are expensive, so try to avoid them if only possible.
// Keys in the `traits_in_scope` cache are always module IDs.
if let Entry::Vacant(entry) = self.traits_in_scope.entry(def_id) {
let module = self.resolver.get_nearest_non_block_module(def_id);
let module_id = module.def_id();
let entry = if module_id == def_id {
Some(entry)
} else if let Entry::Vacant(entry) = self.traits_in_scope.entry(module_id) {
Some(entry)
} else {
None
};
if let Some(entry) = entry {
entry.insert(self.resolver.traits_in_scope(
None,
&ParentScope::module(module, self.resolver),
SyntaxContext::root(),
None,
));
}
}
}
fn add_traits_in_parent_scope(&mut self, def_id: DefId) {
if let Some(module_id) = self.resolver.parent(def_id) {
self.add_traits_in_scope(module_id);
}
}
/// Add traits in scope for links in impls collected by the `collect-intra-doc-links` pass.
/// That pass filters impls using type-based information, but we don't yet have such
/// information here, so we just conservatively calculate traits in scope for *all* modules
/// having impls in them.
fn add_foreign_traits_in_scope(&mut self) {
for cnum in Vec::from_iter(self.resolver.cstore().crates_untracked()) {
let all_traits = Vec::from_iter(self.resolver.cstore().traits_in_crate_untracked(cnum));
let all_trait_impls =
Vec::from_iter(self.resolver.cstore().trait_impls_in_crate_untracked(cnum));
let all_inherent_impls =
Vec::from_iter(self.resolver.cstore().inherent_impls_in_crate_untracked(cnum));
let all_incoherent_impls =
Vec::from_iter(self.resolver.cstore().incoherent_impls_in_crate_untracked(cnum));
// Querying traits in scope is expensive so we try to prune the impl and traits lists
// using privacy, private traits and impls from other crates are never documented in
// the current crate, and links in their doc comments are not resolved.
for &def_id in &all_traits {
if self.resolver.cstore().visibility_untracked(def_id) == Visibility::Public {
self.add_traits_in_parent_scope(def_id);
}
}
for &(trait_def_id, impl_def_id, simplified_self_ty) in &all_trait_impls {
if self.resolver.cstore().visibility_untracked(trait_def_id) == Visibility::Public
&& simplified_self_ty.and_then(|ty| ty.def()).map_or(true, |ty_def_id| {
self.resolver.cstore().visibility_untracked(ty_def_id) == Visibility::Public
})
{
self.add_traits_in_parent_scope(impl_def_id);
}
}
for (ty_def_id, impl_def_id) in all_inherent_impls {
if self.resolver.cstore().visibility_untracked(ty_def_id) == Visibility::Public {
self.add_traits_in_parent_scope(impl_def_id);
}
}
for def_id in all_incoherent_impls {
self.add_traits_in_parent_scope(def_id);
}
self.all_traits.extend(all_traits);
self.all_trait_impls.extend(all_trait_impls.into_iter().map(|(_, def_id, _)| def_id));
}
}
fn load_links_in_attrs(&mut self, attrs: &[ast::Attribute]) {
// FIXME: this needs to consider reexport inlining.
let attrs = clean::Attributes::from_ast(attrs, None);
for (parent_module, doc) in attrs.collapsed_doc_value_by_module_level() {
let module_id = parent_module.unwrap_or(self.current_mod.to_def_id());
self.add_traits_in_scope(module_id);
for link in markdown_links(&doc.as_str()) {
let path_str = if let Some(Ok(x)) = preprocess_link(&link) {
x.path_str
} else {
continue;
};
self.resolver.resolve_rustdoc_path(&path_str, TypeNS, module_id);
}
}
}
/// When reexports are inlined, they are replaced with item which they refer to, those items
/// may have links in their doc comments, those links are resolved at the item definition site,
/// so we need to know traits in scope at that definition site.
fn process_module_children_or_reexports(&mut self, module_id: DefId) {
if !self.visited_mods.insert(module_id) {
return; // avoid infinite recursion
}
for child in self.resolver.module_children_or_reexports(module_id) {
if child.vis == Visibility::Public {
if let Some(def_id) = child.res.opt_def_id() {
self.add_traits_in_parent_scope(def_id);
}
if let Res::Def(DefKind::Mod, module_id) = child.res {
self.process_module_children_or_reexports(module_id);
}
}
}
}
}
impl Visitor<'_> for IntraLinkCrateLoader<'_, '_> {
fn visit_item(&mut self, item: &ast::Item) {
if let ItemKind::Mod(..) = item.kind {
let old_mod = mem::replace(&mut self.current_mod, self.resolver.local_def_id(item.id));
// A module written with a outline doc comments will resolve traits relative
// to the parent module. Make sure the parent module's traits-in-scope are
// loaded, even if the module itself has no doc comments.
self.add_traits_in_parent_scope(self.current_mod.to_def_id());
self.load_links_in_attrs(&item.attrs);
self.process_module_children_or_reexports(self.current_mod.to_def_id());
visit::walk_item(self, item);
self.current_mod = old_mod;
} else {
match item.kind {
ItemKind::Trait(..) => {
self.all_traits.push(self.resolver.local_def_id(item.id).to_def_id());
}
ItemKind::Impl(box ast::Impl { of_trait: Some(..), .. }) => {
self.all_trait_impls.push(self.resolver.local_def_id(item.id).to_def_id());
}
_ => {}
}
self.load_links_in_attrs(&item.attrs);
visit::walk_item(self, item);
}
}
fn visit_assoc_item(&mut self, item: &ast::AssocItem, ctxt: AssocCtxt) {
self.load_links_in_attrs(&item.attrs);
visit::walk_assoc_item(self, item, ctxt)
}
fn visit_foreign_item(&mut self, item: &ast::ForeignItem) {
self.load_links_in_attrs(&item.attrs);
visit::walk_foreign_item(self, item)
}
fn visit_variant(&mut self, v: &ast::Variant) {
self.load_links_in_attrs(&v.attrs);
visit::walk_variant(self, v)
}
fn visit_field_def(&mut self, field: &ast::FieldDef) {
self.load_links_in_attrs(&field.attrs);
visit::walk_field_def(self, field)
}
// NOTE: if doc-comments are ever allowed on other nodes (e.g. function parameters),
// then this will have to implement other visitor methods too.
}