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 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}