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
use rustc::hir::def::Def;
use rustc::hir::def_id::{DefId, CRATE_DEF_INDEX, LOCAL_CRATE};
use rustc::hir::Node;
use rustc::hir::{ForeignMod, Mod};
use rustc::ty::TyCtxt;
use syntax::ast::Ident;
use syntax_pos::symbol::Symbol;

fn push_hir_mod_children(tcx: TyCtxt, m: &Mod, children: &mut Vec<(Symbol, Def)>) {
    use rustc::hir::ItemKind::*;

    for &iid in &m.item_ids {
        let node = tcx.hir().get_by_hir_id(iid.id);
        let item = expect!([node] Node::Item(i) => i);

        match item.node {
            ForeignMod(ref fm) => {
                push_hir_foreign_mod_children(tcx, fm, children);
            }

            ExternCrate(..) => {
                let item_did = tcx.hir().local_def_id_from_hir_id(item.hir_id);
                let krate = tcx
                    .extern_mod_stmt_cnum(item_did)
                    .expect("no cnum available for `extern crate`");
                let krate_did = DefId {
                    krate,
                    index: CRATE_DEF_INDEX,
                };
                // This is a little bogus (the thing at `krate_did` isn't really a module), but it
                // works well enough.
                let krate_def = Def::Mod(krate_did);
                children.push((item.ident.name, krate_def));
            }

            // We could do something clever for `Use`, but it would be a little tricky in the
            // case where the `use` resolves to an extern crate's item.  For now we just use the
            // default logic, which omits the `use` (there is no `Def::Use` variant).
            _ => {
                if let Some(def) = tcx.hir().describe_def_by_hir_id(item.hir_id) {
                    children.push((item.ident.name, def));
                }
            }
        }
    }
}

fn push_hir_foreign_mod_children(tcx: TyCtxt, fm: &ForeignMod, children: &mut Vec<(Symbol, Def)>) {
    for fi in &fm.items {
        if let Some(def) = tcx.hir().describe_def_by_hir_id(fi.hir_id) {
            children.push((fi.ident.name, def));
        }
    }
}

/// List the names and `Def`s of all children of the indicated module.
pub fn module_children(tcx: TyCtxt, did: DefId) -> Vec<(Symbol, Def)> {
    use rustc::hir::ItemKind::*;

    if did.krate == LOCAL_CRATE {
        if did.index == CRATE_DEF_INDEX {
            // Crate root needs some special handling.  Looking it up in the HIR map by DefId
            // fails, but we can grab its root `Mod` directly.
            let m = &tcx.hir().krate().module;
            let mut children = Vec::with_capacity(m.item_ids.len());
            push_hir_mod_children(tcx, m, &mut children);
            return children;
        }

        let node = tcx
            .hir()
            .get_if_local(did)
            .expect("definition not present in HIR map");
        let item = expect!([node] Node::Item(i) => i);

        match item.node {
            ExternCrate(..) => {
                let krate = tcx
                    .extern_mod_stmt_cnum(did)
                    .expect("no cnum available for `extern crate`");
                let krate_did = DefId {
                    krate,
                    index: CRATE_DEF_INDEX,
                };
                module_children(tcx, krate_did)
            }

            Use(ref path, _kind) => {
                let target_did = path.def.def_id();
                module_children(tcx, target_did)
            }

            Mod(ref m) => {
                let mut children = Vec::with_capacity(m.item_ids.len());
                push_hir_mod_children(tcx, m, &mut children);
                children
            }

            ForeignMod(ref fm) => {
                let mut children = Vec::with_capacity(fm.items.len());
                push_hir_foreign_mod_children(tcx, fm, &mut children);
                children
            }

            ref it => panic!("item {:?} does not have resolvable children", it),
        }
    } else {
        let children = tcx.item_children(did);
        children.iter().map(|c| (c.ident.name, c.def)).collect()
    }
}

/// Resolve an absolute path to a `Def`.
pub fn resolve_absolute(tcx: TyCtxt, path: &[Ident]) -> Def {
    let krate_did = DefId {
        krate: LOCAL_CRATE,
        index: CRATE_DEF_INDEX,
    };
    let mut cur_def = Def::Mod(krate_did);

    'a: for ident in path {
        for (sym, def) in module_children(tcx, cur_def.def_id()) {
            if sym == ident.name {
                cur_def = def;
                continue 'a;
            }
        }

        panic!("could not find {:?} in {:?}", ident, cur_def);
    }

    cur_def
}