Skip to main content

lisette_syntax/program/
emit_input.rs

1use rustc_hash::{FxHashMap as HashMap, FxHashSet as HashSet};
2
3use ecow::EcoString;
4
5use crate::ast::{BindingId as AstBindingId, Pattern, RestPattern, Span};
6use crate::types::Symbol;
7
8use super::{Definition, File, ModuleInfo};
9
10#[derive(Debug, Clone, Default)]
11pub struct UnusedInfo {
12    bindings: HashSet<Span>,
13    definitions: HashSet<Span>,
14    pub imports_by_module: HashMap<EcoString, HashSet<EcoString>>,
15}
16
17impl UnusedInfo {
18    pub fn mark_binding_unused(&mut self, span: Span) {
19        self.bindings.insert(span);
20    }
21
22    pub fn is_unused_binding(&self, pattern: &Pattern) -> bool {
23        match pattern {
24            Pattern::Identifier { span, .. } => self.bindings.contains(span),
25            Pattern::AsBinding { span, name, .. } => {
26                let name_span = Span::new(
27                    span.file_id,
28                    span.byte_offset + span.byte_length - name.len() as u32,
29                    name.len() as u32,
30                );
31                self.bindings.contains(&name_span)
32            }
33            _ => false,
34        }
35    }
36
37    pub fn is_unused_rest_binding(&self, rest: &RestPattern) -> bool {
38        match rest {
39            RestPattern::Bind { span, .. } => self.bindings.contains(span),
40            _ => false,
41        }
42    }
43
44    pub fn mark_definition_unused(&mut self, span: Span) {
45        self.definitions.insert(span);
46    }
47
48    pub fn is_unused_definition(&self, span: &Span) -> bool {
49        self.definitions.contains(span)
50    }
51
52    pub fn merge(&mut self, other: UnusedInfo) {
53        let UnusedInfo {
54            bindings,
55            definitions,
56            imports_by_module,
57        } = other;
58        self.bindings.extend(bindings);
59        self.definitions.extend(definitions);
60        for (module, imports) in imports_by_module {
61            self.imports_by_module
62                .entry(module)
63                .or_default()
64                .extend(imports);
65        }
66    }
67}
68
69#[derive(Debug, Clone, Default)]
70pub struct UsableEquals {
71    by_id: HashMap<String, Option<String>>,
72}
73
74impl UsableEquals {
75    pub fn insert(&mut self, id: String, private_to_module: Option<String>) {
76        self.by_id.insert(id, private_to_module);
77    }
78
79    pub fn usable_from(&self, id: &str, current_module: &str) -> bool {
80        Self::entry_usable_from(self.by_id.get(id), current_module)
81    }
82
83    pub fn entry_usable_from(entry: Option<&Option<String>>, current_module: &str) -> bool {
84        match entry {
85            Some(None) => true,
86            Some(Some(module)) => module == current_module,
87            None => false,
88        }
89    }
90}
91
92#[derive(Debug, Clone, Default)]
93pub struct MutationInfo {
94    bindings: HashSet<AstBindingId>,
95}
96
97impl MutationInfo {
98    pub fn mark_binding_mutated(&mut self, id: AstBindingId) {
99        self.bindings.insert(id);
100    }
101
102    pub fn is_mutated(&self, id: AstBindingId) -> bool {
103        self.bindings.contains(&id)
104    }
105}
106
107pub struct EmitInput {
108    pub files: HashMap<u32, File>,
109    pub definitions: HashMap<Symbol, Definition>,
110    pub modules: HashMap<String, ModuleInfo>,
111    pub entry_module_id: String,
112    pub unused: UnusedInfo,
113    pub mutations: MutationInfo,
114    pub cached_modules: HashSet<String>,
115    pub ufcs_methods: HashSet<(String, String)>,
116    pub usable_equals: UsableEquals,
117    pub go_package_names: HashMap<String, String>,
118    pub go_module_ids: HashSet<String>,
119}
120
121#[cfg(test)]
122mod tests {
123    use super::*;
124
125    fn span(offset: u32) -> Span {
126        Span::new(0, offset, 1)
127    }
128
129    #[test]
130    fn merge_extends_bindings_definitions_and_imports() {
131        let mut a = UnusedInfo::default();
132        a.mark_binding_unused(span(0));
133        a.mark_definition_unused(span(1));
134        a.imports_by_module
135            .insert("m1".into(), HashSet::from_iter(["x".into()]));
136
137        let mut b = UnusedInfo::default();
138        b.mark_binding_unused(span(2));
139        b.mark_definition_unused(span(3));
140        b.imports_by_module
141            .insert("m1".into(), HashSet::from_iter(["y".into()]));
142        b.imports_by_module
143            .insert("m2".into(), HashSet::from_iter(["z".into()]));
144
145        a.merge(b);
146
147        assert_eq!(a.bindings.len(), 2);
148        assert_eq!(a.definitions.len(), 2);
149        assert_eq!(a.imports_by_module["m1"].len(), 2);
150        assert_eq!(a.imports_by_module["m2"].len(), 1);
151    }
152}