Skip to main content

frs/cmds/rsl/rules/common/
module_idx.rs

1use std::collections::HashMap;
2use std::collections::HashSet;
3use std::collections::VecDeque;
4
5use syn::Item;
6
7use super::import_resolution::ImportBinding;
8
9pub struct ModuleScope<'ast> {
10    pub path: Vec<String>,
11    pub items: &'ast [Item],
12}
13
14#[derive(Default)]
15pub struct ScopeInfo {
16    pub definitions: HashSet<String>,
17    pub fns: HashSet<String>,
18    pub glob_imports: Vec<Vec<String>>,
19    pub imports: Vec<ImportBinding>,
20    pub unknown_imports: bool,
21}
22
23pub struct ModuleIdx<'ast> {
24    pub scopes: Vec<ModuleScope<'ast>>,
25    pub info: HashMap<Vec<String>, ScopeInfo>,
26    pub modules: HashSet<Vec<String>>,
27}
28
29pub fn module_idx(file: &syn::File) -> ModuleIdx<'_> {
30    let mut idx = ModuleIdx {
31        scopes: Vec::new(),
32        info: HashMap::new(),
33        modules: HashSet::new(),
34    };
35    let mut pending = VecDeque::from([(Vec::new(), file.items.as_slice())]);
36
37    while let Some((path, items)) = pending.pop_front() {
38        let mut info = ScopeInfo::default();
39        for item in items {
40            if let Some(name) = self::item_name(item) {
41                info.definitions.insert(name);
42            }
43            if let Item::Fn(fn_item) = item {
44                info.fns.insert(fn_item.sig.ident.to_string());
45            }
46            if let Item::Mod(module) = item {
47                let mut nested_path = path.clone();
48                nested_path.push(module.ident.to_string());
49                idx.modules.insert(nested_path.clone());
50                if let Some((_, nested_items)) = &module.content {
51                    pending.push_back((nested_path, nested_items.as_slice()));
52                }
53            }
54            if let Item::Use(item_use) = item {
55                let bindings = super::import_resolution::use_bindings(&item_use.tree, &path);
56                info.imports.extend(bindings.bindings);
57                info.glob_imports.extend(bindings.glob_imports);
58                info.unknown_imports |= bindings.unknown;
59            }
60        }
61        idx.info.insert(path.clone(), info);
62        idx.scopes.push(ModuleScope { path, items });
63    }
64
65    idx
66}
67
68fn item_name(item: &Item) -> Option<String> {
69    match item {
70        Item::Const(item) => Some(item.ident.to_string()),
71        Item::Enum(item) => Some(item.ident.to_string()),
72        Item::ExternCrate(item) => Some(item.ident.to_string()),
73        Item::Fn(item) => Some(item.sig.ident.to_string()),
74        Item::Mod(item) => Some(item.ident.to_string()),
75        Item::Static(item) => Some(item.ident.to_string()),
76        Item::Struct(item) => Some(item.ident.to_string()),
77        Item::Trait(item) => Some(item.ident.to_string()),
78        Item::TraitAlias(item) => Some(item.ident.to_string()),
79        Item::Type(item) => Some(item.ident.to_string()),
80        Item::Union(item) => Some(item.ident.to_string()),
81        Item::ForeignMod(_) | Item::Impl(_) | Item::Macro(_) | Item::Use(_) | Item::Verbatim(_) | _ => None,
82    }
83}
84
85pub fn is_local_module_path(idx: &ModuleIdx<'_>, path: &[String]) -> bool {
86    path.is_empty() || idx.modules.contains(path)
87}