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

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

// 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 struct IntraLinkCrateLoader {
    current_mod: DefId,
    crate resolver: Rc<RefCell<interface::BoxedResolver>>,
}

impl IntraLinkCrateLoader {
    crate fn new(resolver: Rc<RefCell<interface::BoxedResolver>>) -> Self {
        let crate_id = LocalDefId { local_def_index: CRATE_DEF_INDEX }.to_def_id();
        Self { current_mod: crate_id, resolver }
    }
}

impl ast::visit::Visitor<'_> for IntraLinkCrateLoader {
    fn visit_attribute(&mut self, attr: &ast::Attribute) {
        use crate::html::markdown::markdown_links;
        use crate::passes::collect_intra_doc_links::preprocess_link;

        if let Some(doc) = attr.doc_str() {
            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.borrow_mut().access(|resolver| {
                    let _ = resolver.resolve_str_path_error(
                        attr.span,
                        &path_str,
                        TypeNS,
                        self.current_mod,
                    );
                });
            }
        }
        ast::visit::walk_attribute(self, attr);
    }

    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.to_def_id());
            ast::visit::walk_item(self, item);
            self.current_mod = old_mod;
        } else {
            ast::visit::walk_item(self, item);
        }
    }
}