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 MutationInfo {
71    bindings: HashSet<AstBindingId>,
72}
73
74impl MutationInfo {
75    pub fn mark_binding_mutated(&mut self, id: AstBindingId) {
76        self.bindings.insert(id);
77    }
78
79    pub fn is_mutated(&self, id: AstBindingId) -> bool {
80        self.bindings.contains(&id)
81    }
82}
83
84pub struct EmitInput {
85    pub files: HashMap<u32, File>,
86    pub definitions: HashMap<Symbol, Definition>,
87    pub modules: HashMap<String, ModuleInfo>,
88    pub entry_module_id: String,
89    pub unused: UnusedInfo,
90    pub mutations: MutationInfo,
91    pub cached_modules: HashSet<String>,
92    pub ufcs_methods: HashSet<(String, String)>,
93    pub go_package_names: HashMap<String, String>,
94    pub go_module_ids: HashSet<String>,
95}
96
97#[cfg(test)]
98mod tests {
99    use super::*;
100
101    fn span(offset: u32) -> Span {
102        Span::new(0, offset, 1)
103    }
104
105    #[test]
106    fn merge_extends_bindings_definitions_and_imports() {
107        let mut a = UnusedInfo::default();
108        a.mark_binding_unused(span(0));
109        a.mark_definition_unused(span(1));
110        a.imports_by_module
111            .insert("m1".into(), HashSet::from_iter(["x".into()]));
112
113        let mut b = UnusedInfo::default();
114        b.mark_binding_unused(span(2));
115        b.mark_definition_unused(span(3));
116        b.imports_by_module
117            .insert("m1".into(), HashSet::from_iter(["y".into()]));
118        b.imports_by_module
119            .insert("m2".into(), HashSet::from_iter(["z".into()]));
120
121        a.merge(b);
122
123        assert_eq!(a.bindings.len(), 2);
124        assert_eq!(a.definitions.len(), 2);
125        assert_eq!(a.imports_by_module["m1"].len(), 2);
126        assert_eq!(a.imports_by_module["m2"].len(), 1);
127    }
128}