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
use rustc_ast as ast;
use rustc_hir::def::Namespace::TypeNS;
use rustc_hir::def_id::{LocalDefId, CRATE_DEF_ID};
use rustc_interface::interface;
use rustc_span::Span;

use std::cell::RefCell;
use std::mem;
use std::rc::Rc;

type Resolver = Rc<RefCell<interface::BoxedResolver>>;
// Letting the resolver escape at the end of the function leads to inconsistencies between the
// crates the TyCtxt sees and the resolver sees (because the resolver could load more crates
// after escaping). Hopefully `IntraLinkCrateLoader` gets all the crates we need ...
crate fn load_intra_link_crates(resolver: Resolver, krate: &ast::Crate) -> Resolver {
    let mut loader = IntraLinkCrateLoader { current_mod: CRATE_DEF_ID, resolver };
    // `walk_crate` doesn't visit the crate itself for some reason.
    loader.load_links_in_attrs(&krate.attrs, krate.span);
    ast::visit::walk_crate(&mut loader, krate);
    loader.resolver
}

struct IntraLinkCrateLoader {
    current_mod: LocalDefId,
    resolver: Rc<RefCell<interface::BoxedResolver>>,
}

impl IntraLinkCrateLoader {
    fn load_links_in_attrs(&mut self, attrs: &[ast::Attribute], span: Span) {
        use crate::html::markdown::markdown_links;
        use crate::passes::collect_intra_doc_links::preprocess_link;

        // FIXME: this probably needs to consider inlining
        let attrs = crate::clean::Attributes::from_ast(attrs, None);
        for (parent_module, doc) in attrs.collapsed_doc_value_by_module_level() {
            debug!(?doc);
            for link in markdown_links(&doc.as_str()) {
                debug!(?link.link);
                let path_str = if let Some(Ok(x)) = preprocess_link(&link) {
                    x.path_str
                } else {
                    continue;
                };
                self.resolver.borrow_mut().access(|resolver| {
                    let _ = resolver.resolve_str_path_error(
                        span,
                        &path_str,
                        TypeNS,
                        parent_module.unwrap_or(self.current_mod.to_def_id()),
                    );
                });
            }
        }
    }
}

impl ast::visit::Visitor<'_> for IntraLinkCrateLoader {
    fn visit_item(&mut self, item: &ast::Item) {
        use rustc_ast_lowering::ResolverAstLowering;

        if let ast::ItemKind::Mod(..) = item.kind {
            let new_mod =
                self.resolver.borrow_mut().access(|resolver| resolver.local_def_id(item.id));
            let old_mod = mem::replace(&mut self.current_mod, new_mod);

            self.load_links_in_attrs(&item.attrs, item.span);
            ast::visit::walk_item(self, item);

            self.current_mod = old_mod;
        } else {
            self.load_links_in_attrs(&item.attrs, item.span);
            ast::visit::walk_item(self, item);
        }
    }
}