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
//! A `ModuleGroup` describes a grouping of modules that together form an assembly.
use mun_hir::{HasVisibility, HirDatabase};
use rustc_hash::{FxHashMap, FxHashSet};
use std::hash::{Hash, Hasher};
use std::iter::FromIterator;
/// A `ModuleGroup` describes a grouping of modules
#[derive(Clone, Eq, Debug)]
pub struct ModuleGroup {
ordered_modules: Vec<mun_hir::Module>,
modules: FxHashSet<mun_hir::Module>,
includes_entire_subtree: FxHashMap<mun_hir::Module, bool>,
pub name: String,
}
impl Hash for ModuleGroup {
fn hash<H: Hasher>(&self, state: &mut H) {
self.ordered_modules.hash(state);
self.name.hash(state);
}
}
impl PartialEq for ModuleGroup {
fn eq(&self, other: &Self) -> bool {
self.ordered_modules == other.ordered_modules && self.name == other.name
}
}
impl ModuleGroup {
/// Constructs a new `ModuleGroup` from a collection of modules and a name.
pub fn new(
db: &dyn HirDatabase,
name: String,
modules: impl IntoIterator<Item = mun_hir::Module>,
) -> Self {
let modules = FxHashSet::from_iter(modules);
let includes_entire_subtree = modules
.iter()
.map(|&module| {
(
module,
module
.children(db)
.iter()
.all(|child_module| modules.contains(child_module)),
)
})
.collect();
let mut ordered_modules: Vec<mun_hir::Module> = modules.iter().copied().collect();
ordered_modules.sort();
Self {
ordered_modules,
modules,
includes_entire_subtree,
name,
}
}
/// Constructs a new module group from a single module
pub fn from_single_module(db: &dyn HirDatabase, module: mun_hir::Module) -> Self {
Self::new(db, module.full_name(db), vec![module])
}
/// Returns true if the specified `mun_hir::Module` is part of this group.
pub fn contains(&self, module: mun_hir::Module) -> bool {
self.modules.contains(&module)
}
/// Returns an iterator over all modules in the group
pub fn iter(&self) -> impl Iterator<Item = mun_hir::Module> + '_ {
self.ordered_modules.iter().copied()
}
/// Returns true if the specified function should be exported from the module group. This
/// indicates that when queried the resulting assembly will expose this function.
pub fn should_export_fn(&self, db: &dyn HirDatabase, function: mun_hir::Function) -> bool {
// If the function is not defined in the module group we should definitely not export it.
if !self.modules.contains(&function.module(db)) {
return false;
}
let vis = function.visibility(db);
match vis {
// If the function is publicly accessible it must always be exported
mun_hir::Visibility::Public => true,
// The function is visible from the specified module and all child modules.
mun_hir::Visibility::Module(visible_mod) => {
// If the modules is contained within `includes_entire_subtree` it is included in
// the module group.
self.includes_entire_subtree
.get(&visible_mod.into())
// If all its children are also part of the module group we can keep the
// function internal, so there is no need to export it.
.map(|&includes_subtree| !includes_subtree)
// Otherwise, the module is not part of the group and we have to export it.
.unwrap_or(true)
}
}
}
/// Returns true if the specified function should be included in the dispatch table of this
/// module group if it is used from within this module group.
pub fn should_runtime_link_fn(
&self,
db: &dyn HirDatabase,
function: mun_hir::Function,
) -> bool {
function.is_extern(db) || !self.modules.contains(&function.module(db))
}
/// Returns the `mun_hir::FileId`s that are included in this module group.
pub fn files<'s>(
&'s self,
db: &'s dyn HirDatabase,
) -> impl Iterator<Item = mun_hir::FileId> + 's {
self.ordered_modules
.iter()
.filter_map(move |module| module.file_id(db))
}
/// Returns the filename for this module group
pub fn relative_file_path(&self) -> mun_paths::RelativePathBuf {
mun_paths::RelativePathBuf::from(self.name.replace("::", "$"))
}
}