lisette_syntax/program/
emit_input.rs1use 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}